summaryrefslogtreecommitdiff
path: root/serial
diff options
context:
space:
mode:
authorDave Wilson <[email protected]>2015-03-17 19:50:07 -0700
committerDave Wilson <[email protected]>2015-03-17 19:50:07 -0700
commit97cf5197cf5b882b2c689d8dc2b555f2edf8f418 (patch)
tree46f3701832d70b420eb0fc0eb93261f9da45db3f /serial
parentef1905bf1e8825bb31120dfb27e0daf3154d859a (diff)
Initial publish
Diffstat (limited to 'serial')
-rw-r--r--serial/VirtualSerial/ComPort/exports.def6
-rw-r--r--serial/VirtualSerial/ComPort/virtualserial.inx78
-rw-r--r--serial/VirtualSerial/ComPort/virtualserial.rc17
-rw-r--r--serial/VirtualSerial/ComPort/virtualserial.vcxproj256
-rw-r--r--serial/VirtualSerial/ComPort/virtualserial.vcxproj.Filters57
-rw-r--r--serial/VirtualSerial/FakeModem/exports.def6
-rw-r--r--serial/VirtualSerial/FakeModem/fakemodem.inx202
-rw-r--r--serial/VirtualSerial/FakeModem/fakemodem.rc17
-rw-r--r--serial/VirtualSerial/FakeModem/fakemodem.vcxproj256
-rw-r--r--serial/VirtualSerial/FakeModem/fakemodem.vcxproj.Filters57
-rw-r--r--serial/VirtualSerial/ReadMe.md59
-rw-r--r--serial/VirtualSerial/VirtualSerial.sln46
-rw-r--r--serial/VirtualSerial/comsup.cpp344
-rw-r--r--serial/VirtualSerial/comsup.h215
-rw-r--r--serial/VirtualSerial/device.cpp674
-rw-r--r--serial/VirtualSerial/device.h261
-rw-r--r--serial/VirtualSerial/dllsup.cpp177
-rw-r--r--serial/VirtualSerial/driver.cpp220
-rw-r--r--serial/VirtualSerial/driver.h149
-rw-r--r--serial/VirtualSerial/internal.h133
-rw-r--r--serial/VirtualSerial/queue.cpp1188
-rw-r--r--serial/VirtualSerial/queue.h206
-rw-r--r--serial/VirtualSerial/ringbuffer.cpp258
-rw-r--r--serial/VirtualSerial/ringbuffer.h223
-rw-r--r--serial/VirtualSerial/serial.h48
-rw-r--r--serial/serenum/ReadMe.md39
-rw-r--r--serial/serenum/SerEnum_sample.vcxproj219
-rw-r--r--serial/serenum/SerEnum_sample.vcxproj.Filters49
-rw-r--r--serial/serenum/enum.c1834
-rw-r--r--serial/serenum/log.c185
-rw-r--r--serial/serenum/log.h57
-rw-r--r--serial/serenum/pch.h15
-rw-r--r--serial/serenum/pchsrc.c1
-rw-r--r--serial/serenum/pnp.c1505
-rw-r--r--serial/serenum/power.c299
-rw-r--r--serial/serenum/serenum.c766
-rw-r--r--serial/serenum/serenum.h626
-rw-r--r--serial/serenum/serenum.rc16
-rw-r--r--serial/serenum/serenum.sln28
-rw-r--r--serial/serenum/string.c1029
-rw-r--r--serial/serial/ReadMe.md35
-rw-r--r--serial/serial/error.c67
-rw-r--r--serial/serial/flush.c86
-rw-r--r--serial/serial/immediat.c458
-rw-r--r--serial/serial/initunlo.c197
-rw-r--r--serial/serial/ioctl.c2195
-rw-r--r--serial/serial/isr.c1517
-rw-r--r--serial/serial/log.c97
-rw-r--r--serial/serial/log.h37
-rw-r--r--serial/serial/modmflow.c1714
-rw-r--r--serial/serial/openclos.c850
-rw-r--r--serial/serial/pnp.c2800
-rw-r--r--serial/serial/power.c331
-rw-r--r--serial/serial/precomp.h18
-rw-r--r--serial/serial/precompsrc.c1
-rw-r--r--serial/serial/purge.c175
-rw-r--r--serial/serial/qsfile.c180
-rw-r--r--serial/serial/read.c1748
-rw-r--r--serial/serial/registry.c443
-rw-r--r--serial/serial/serial.h1757
-rw-r--r--serial/serial/serial.inx111
-rw-r--r--serial/serial/serial.rc14
-rw-r--r--serial/serial/serial.sln28
-rw-r--r--serial/serial/serialp.h596
-rw-r--r--serial/serial/serlog.mc290
-rw-r--r--serial/serial/trace.h118
-rw-r--r--serial/serial/utils.c1946
-rw-r--r--serial/serial/waitmask.c574
-rw-r--r--serial/serial/wdfserial.vcxproj389
-rw-r--r--serial/serial/wdfserial.vcxproj.Filters99
-rw-r--r--serial/serial/wmi.c295
-rw-r--r--serial/serial/write.c1195
72 files changed, 32182 insertions, 0 deletions
diff --git a/serial/VirtualSerial/ComPort/exports.def b/serial/VirtualSerial/ComPort/exports.def
new file mode 100644
index 00000000..5d92ff03
--- /dev/null
+++ b/serial/VirtualSerial/ComPort/exports.def
@@ -0,0 +1,6 @@
+; exports.def : Declares the module parameters.
+
+LIBRARY "Virtualserial.DLL"
+
+EXPORTS
+ DllGetClassObject PRIVATE
diff --git a/serial/VirtualSerial/ComPort/virtualserial.inx b/serial/VirtualSerial/ComPort/virtualserial.inx
new file mode 100644
index 00000000..4242388c
--- /dev/null
+++ b/serial/VirtualSerial/ComPort/virtualserial.inx
@@ -0,0 +1,78 @@
+;
+; VirtualSerial.inf
+;
+
+[Version]
+Signature="$Windows NT$"
+Class=Ports
+ClassGuid={4D36E978-E325-11CE-BFC1-08002BE10318}
+Provider=%MSFTUMDF%
+CatalogFile=WUDF.cat
+DriverVer=03/25/2005,0.0.0.1
+
+[Manufacturer]
+%MSFTUMDF%=Microsoft,NT$ARCH$
+
+[Microsoft.NT$ARCH$]
+%VirtualSerialDeviceName%=VirtualSerial_Install,UMDF\VirtualSerial
+
+[SourceDisksFiles]
+Virtualserial.dll=1
+WudfUpdate_$UMDFCOINSTALLERVERSION$.dll=1
+
+[SourceDisksNames]
+1 = %MediaDescription%
+
+; =================== UMDF VirtualSerial Device ==================================
+
+[VirtualSerial_Install.NT]
+CopyFiles=UMDriverCopy
+
+[VirtualSerial_Install.NT.hw]
+
+[VirtualSerial_Install.NT.Services]
+AddService=WUDFRd,0x000001fa,WUDFRD_ServiceInstall
+
+[VirtualSerial_Install.NT.CoInstallers]
+CopyFiles=CoInstallers_CopyFiles
+AddReg=CoInstallers_AddReg
+
+[VirtualSerial_Install.NT.Wdf]
+UmdfService=VirtualSerial,VirtualSerial_Install
+UmdfServiceOrder=VirtualSerial
+UmdfKernelModeClientPolicy = AllowKernelModeClients
+UmdfFileObjectPolicy = AllowNullAndUnknownFileObjects
+UmdfFsContextUsePolicy = CannotUseFsContexts
+
+[VirtualSerial_Install]
+UmdfLibraryVersion=$UMDFVERSION$
+ServiceBinary=%12%\UMDF\Virtualserial.dll
+DriverCLSID={C8ECC087-6B79-4de5-8FB4-C03358A29617}
+
+[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]
+Virtualserial.dll
+
+; =================== Generic ==================================
+
+[Strings]
+MSFTUMDF="Microsoft"
+MediaDescription="Microsoft Sample Driver Installation Media"
+WudfRdDisplayName="Windows Driver Foundation - User-mode Driver Framework Reflector"
+VirtualSerialDeviceName="Microsoft VirtualSerial User-Mode Device Sample"
diff --git a/serial/VirtualSerial/ComPort/virtualserial.rc b/serial/VirtualSerial/ComPort/virtualserial.rc
new file mode 100644
index 00000000..eeda38a7
--- /dev/null
+++ b/serial/VirtualSerial/ComPort/virtualserial.rc
@@ -0,0 +1,17 @@
+//---------------------------------------------------------------------------
+// Virtualserial.rc
+//
+// Copyright (c) Microsoft Corporation, All Rights Reserved
+//---------------------------------------------------------------------------
+
+
+#include <windows.h>
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_DLL
+#define VER_FILESUBTYPE VFT_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "WDF:UMDF VirtualSerial User-Mode Driver Sample"
+#define VER_INTERNALNAME_STR "VirtualSerial"
+#define VER_ORIGINALFILENAME_STR "Virtualserial.dll"
+
+#include "common.ver"
diff --git a/serial/VirtualSerial/ComPort/virtualserial.vcxproj b/serial/VirtualSerial/ComPort/virtualserial.vcxproj
new file mode 100644
index 00000000..66943fc2
--- /dev/null
+++ b/serial/VirtualSerial/ComPort/virtualserial.vcxproj
@@ -0,0 +1,256 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{EFB282A8-A881-44E7-964D-F068A4FD19C9}</ProjectGuid>
+ <RootNamespace>$(MSBuildProjectName)</RootNamespace>
+ <UMDF_VERSION_MAJOR>1</UMDF_VERSION_MAJOR>
+ <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration>
+ <Platform Condition="'$(Platform)' == ''">Win32</Platform>
+ <SampleGuid>{BA45F248-4EC7-4AF6-A0B1-438FABDF4722}</SampleGuid>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>False</UseDebugLibraries>
+ <DriverTargetPlatform>Desktop</DriverTargetPlatform>
+ <DriverType>UMDF</DriverType>
+ <PlatformToolset>WindowsUserModeDriver10.0</PlatformToolset>
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>True</UseDebugLibraries>
+ <DriverTargetPlatform>Desktop</DriverTargetPlatform>
+ <DriverType>UMDF</DriverType>
+ <PlatformToolset>WindowsUserModeDriver10.0</PlatformToolset>
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>False</UseDebugLibraries>
+ <DriverTargetPlatform>Desktop</DriverTargetPlatform>
+ <DriverType>UMDF</DriverType>
+ <PlatformToolset>WindowsUserModeDriver10.0</PlatformToolset>
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>True</UseDebugLibraries>
+ <DriverTargetPlatform>Desktop</DriverTargetPlatform>
+ <DriverType>UMDF</DriverType>
+ <PlatformToolset>WindowsUserModeDriver10.0</PlatformToolset>
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <PropertyGroup>
+ <OutDir>$(IntDir)</OutDir>
+ </PropertyGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ItemGroup Label="WrappedTaskItems">
+ <ClCompile Include="..\dllsup.cpp; ..\comsup.cpp; ..\driver.cpp; ..\device.cpp; ..\queue.cpp; ..\ringbuffer.cpp">
+ <WppEnabled>true</WppEnabled>
+ <WppDllMacro>true</WppDllMacro>
+ <WppScanConfigurationData>..\internal.h</WppScanConfigurationData>
+ </ClCompile>
+ <Inf Include="virtualserial.inx">
+ <Architecture>$(InfArch)</Architecture>
+ <SpecifyArchitecture>true</SpecifyArchitecture>
+ <CopyOutput>.\$(IntDir)\virtualserial.inf</CopyOutput>
+ </Inf>
+ <OtherWpp Include="virtualserial.rc">
+ <WppEnabled>true</WppEnabled>
+ <WppDllMacro>true</WppDllMacro>
+ <WppScanConfigurationData>..\internal.h</WppScanConfigurationData>
+ </OtherWpp>
+ </ItemGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <TargetName>virtualserial</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <TargetName>virtualserial</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <TargetName>virtualserial</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <TargetName>virtualserial</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);_UNICODE;UNICODE</PreprocessorDefinitions>
+ </ClCompile>
+ <Midl>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);_UNICODE;UNICODE</PreprocessorDefinitions>
+ </Midl>
+ <ResourceCompile>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);_UNICODE;UNICODE</PreprocessorDefinitions>
+ </ResourceCompile>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);_UNICODE;UNICODE</PreprocessorDefinitions>
+ </ClCompile>
+ <Midl>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);_UNICODE;UNICODE</PreprocessorDefinitions>
+ </Midl>
+ <ResourceCompile>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);_UNICODE;UNICODE</PreprocessorDefinitions>
+ </ResourceCompile>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);_UNICODE;UNICODE</PreprocessorDefinitions>
+ </ClCompile>
+ <Midl>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);_UNICODE;UNICODE</PreprocessorDefinitions>
+ </Midl>
+ <ResourceCompile>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);_UNICODE;UNICODE</PreprocessorDefinitions>
+ </ResourceCompile>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);_UNICODE;UNICODE</PreprocessorDefinitions>
+ </ClCompile>
+ <Midl>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);_UNICODE;UNICODE</PreprocessorDefinitions>
+ </Midl>
+ <ResourceCompile>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);_UNICODE;UNICODE</PreprocessorDefinitions>
+ </ResourceCompile>
+ </ItemDefinitionGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <WIN32_WINNT_VERSION>0x0A00</WIN32_WINNT_VERSION>
+ <NTDDI_VERSION>0x0A000000</NTDDI_VERSION>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <WIN32_WINNT_VERSION>0x0A00</WIN32_WINNT_VERSION>
+ <NTDDI_VERSION>0x0A000000</NTDDI_VERSION>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <WIN32_WINNT_VERSION>0x0A00</WIN32_WINNT_VERSION>
+ <NTDDI_VERSION>0x0A000000</NTDDI_VERSION>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <WIN32_WINNT_VERSION>0x0A00</WIN32_WINNT_VERSION>
+ <NTDDI_VERSION>0x0A000000</NTDDI_VERSION>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Link>
+ <EntryPointSymbol Condition="'$(Platform)'=='win32'">_DllMainCRTStartup@12</EntryPointSymbol>
+ <EntryPointSymbol Condition="'$(Platform)'!='win32'">_DllMainCRTStartup</EntryPointSymbol>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Link>
+ <EntryPointSymbol Condition="'$(Platform)'=='win32'">_DllMainCRTStartup@12</EntryPointSymbol>
+ <EntryPointSymbol Condition="'$(Platform)'!='win32'">_DllMainCRTStartup</EntryPointSymbol>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Link>
+ <EntryPointSymbol Condition="'$(Platform)'=='win32'">_DllMainCRTStartup@12</EntryPointSymbol>
+ <EntryPointSymbol Condition="'$(Platform)'!='win32'">_DllMainCRTStartup</EntryPointSymbol>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Link>
+ <EntryPointSymbol Condition="'$(Platform)'=='win32'">_DllMainCRTStartup@12</EntryPointSymbol>
+ <EntryPointSymbol Condition="'$(Platform)'!='win32'">_DllMainCRTStartup</EntryPointSymbol>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <WarningLevel>Level4</WarningLevel>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\strsafe.lib;$(SDK_LIB_PATH)\ntdll.lib;$(SDK_LIB_PATH)\kernel32.lib;$(SDK_LIB_PATH)\advapi32.lib;$(SDK_LIB_PATH)\Ole32.lib;$(SDK_LIB_PATH)\setupapi.lib</AdditionalDependencies>
+ <ModuleDefinitionFile>exports.def</ModuleDefinitionFile>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <WarningLevel>Level4</WarningLevel>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\strsafe.lib;$(SDK_LIB_PATH)\ntdll.lib;$(SDK_LIB_PATH)\kernel32.lib;$(SDK_LIB_PATH)\advapi32.lib;$(SDK_LIB_PATH)\Ole32.lib;$(SDK_LIB_PATH)\setupapi.lib</AdditionalDependencies>
+ <ModuleDefinitionFile>exports.def</ModuleDefinitionFile>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <WarningLevel>Level4</WarningLevel>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\strsafe.lib;$(SDK_LIB_PATH)\ntdll.lib;$(SDK_LIB_PATH)\kernel32.lib;$(SDK_LIB_PATH)\advapi32.lib;$(SDK_LIB_PATH)\Ole32.lib;$(SDK_LIB_PATH)\setupapi.lib</AdditionalDependencies>
+ <ModuleDefinitionFile>exports.def</ModuleDefinitionFile>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <WarningLevel>Level4</WarningLevel>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\strsafe.lib;$(SDK_LIB_PATH)\ntdll.lib;$(SDK_LIB_PATH)\kernel32.lib;$(SDK_LIB_PATH)\advapi32.lib;$(SDK_LIB_PATH)\Ole32.lib;$(SDK_LIB_PATH)\setupapi.lib</AdditionalDependencies>
+ <ModuleDefinitionFile>exports.def</ModuleDefinitionFile>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ResourceCompile Include="virtualserial.rc" />
+ </ItemGroup>
+ <ItemGroup>
+ <Inf Exclude="@(Inf)" Include="*.inf" />
+ <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" />
+ <FilesToPackage Include="@(Inf->'%(CopyOutput)')" Condition="'@(Inf)'!=''" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Exclude="@(None)" Include="*.txt;*.htm;*.html" />
+ <None Exclude="@(None)" Include="*.ico;*.cur;*.bmp;*.dlg;*.rct;*.gif;*.jpg;*.jpeg;*.wav;*.jpe;*.tiff;*.tif;*.png;*.rc2" />
+ <None Exclude="@(None)" Include="*.def;*.bat;*.hpj;*.asmx" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+</Project> \ No newline at end of file
diff --git a/serial/VirtualSerial/ComPort/virtualserial.vcxproj.Filters b/serial/VirtualSerial/ComPort/virtualserial.vcxproj.Filters
new file mode 100644
index 00000000..43e9f856
--- /dev/null
+++ b/serial/VirtualSerial/ComPort/virtualserial.vcxproj.Filters
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions>
+ <UniqueIdentifier>{2B1C4ECC-41B3-4BE4-B6AB-A37A6988B553}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files">
+ <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
+ <UniqueIdentifier>{FD1A6174-0DDB-456B-B047-A0BE30AC7612}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Resource Files">
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions>
+ <UniqueIdentifier>{75CE36CB-D686-4BF7-8BD9-F7275C1AC174}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Driver Files">
+ <Extensions>inf;inv;inx;mof;mc;</Extensions>
+ <UniqueIdentifier>{05B7B9F6-1D0D-4F2D-BAA2-C6E6B1E74EE1}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\comsup.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\device.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\dllsup.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\driver.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\queue.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\ringbuffer.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <None Include="exports.def">
+ <Filter>Source Files</Filter>
+ </None>
+ </ItemGroup>
+ <ItemGroup>
+ <FilesToPackage Include=".\Debug\\virtualserial.inf">
+ <Filter>Driver Files</Filter>
+ </FilesToPackage>
+ <Inf Include="virtualserial.inx">
+ <Filter>Driver Files</Filter>
+ </Inf>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="virtualserial.rc">
+ <Filter>Resource Files</Filter>
+ </ResourceCompile>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/serial/VirtualSerial/FakeModem/exports.def b/serial/VirtualSerial/FakeModem/exports.def
new file mode 100644
index 00000000..48845d0c
--- /dev/null
+++ b/serial/VirtualSerial/FakeModem/exports.def
@@ -0,0 +1,6 @@
+; exports.def : Declares the module parameters.
+
+LIBRARY "FakeModem.DLL"
+
+EXPORTS
+ DllGetClassObject PRIVATE
diff --git a/serial/VirtualSerial/FakeModem/fakemodem.inx b/serial/VirtualSerial/FakeModem/fakemodem.inx
new file mode 100644
index 00000000..be83ca64
--- /dev/null
+++ b/serial/VirtualSerial/FakeModem/fakemodem.inx
@@ -0,0 +1,202 @@
+;
+; FakeModem.inf
+; "Fakemodem" Controllerless driver illustrative example
+;
+
+[Version]
+Signature="$Windows NT$"
+Class=Modem
+ClassGuid={4D36E96D-E325-11CE-BFC1-08002BE10318}
+Provider=%MSFTUMDF%
+CatalogFile=WUDF.cat
+DriverVer=12/30/2010,0.0.0.1
+
+[Manufacturer]
+%MSFTUMDF%=Microsoft,NT$ARCH$
+
+[Microsoft.NT$ARCH$]
+%FakeModemDeviceName%=FakeModem_Install,{b85b7c50-6a01-11d2-b841-00c04fad5171}\fakemodem
+
+[SourceDisksFiles]
+FakeModem.dll=1
+WudfUpdate_$UMDFCOINSTALLERVERSION$.dll=1
+
+[SourceDisksNames]
+1 = %MediaDescription%
+
+; =================== UMDF FakeModem Device ==================================
+
+; The AddReg line below instructs the installer to add the registry
+; entries listed in the following sections of this INF file:
+; [All], [MfgAddReg], [ExtraCRLFResponses], [ModemX.AddReg], [INTERNAL]
+
+[FakeModem_Install.NT]
+CopyFiles=UMDriverCopy
+AddReg = All, MfgAddReg, ExtraCRLFResponses, ModemX.AddReg, INTERNAL
+
+[FakeModem_Install.NT.hw]
+AddReg = LowerFilterAddReg
+
+[LowerFilterAddReg]
+HKR,,"LowerFilters",0x00010000,WUDFRd
+
+[FakeModem_Install.NT.Services]
+AddService=WUDFRd,0x00000000,WUDFRD_ServiceInstall
+
+[WUDFRD_ServiceInstall]
+DisplayName = %WudfRdDisplayName%
+ServiceType = 1
+StartType = 3
+ErrorControl = 1
+ServiceBinary = %12%\WUDFRd.sys
+
+[FakeModem_Install.NT.CoInstallers]
+CopyFiles=CoInstallers_CopyFiles
+AddReg=CoInstallers_AddReg
+
+[CoInstallers_CopyFiles]
+WudfUpdate_$UMDFCOINSTALLERVERSION$.dll
+
+[CoInstallers_AddReg]
+HKR,,CoInstallers32,0x00010000,"WUDFUpdate_$UMDFCOINSTALLERVERSION$.dll"
+
+[FakeModem_Install.NT.Wdf]
+UmdfService=FakeModem,FakeModem_Install
+UmdfServiceOrder=FakeModem
+UmdfKernelModeClientPolicy = AllowKernelModeClients
+UmdfFsContextUsePolicy = CannotUseFsContexts
+
+[FakeModem_Install]
+UmdfLibraryVersion=$UMDFVERSION$
+ServiceBinary=%12%\UMDF\FakeModem.dll
+DriverCLSID={C8ECC087-6B79-4de5-8FB4-C03358A29617}
+
+[DestinationDirs]
+UMDriverCopy=12,UMDF ; copy to drivers\UMDF
+CoInstallers_CopyFiles=11
+
+[UMDriverCopy]
+FakeModem.dll
+
+;-------------------------------------------------------------------------------------------------------
+; DDK procalc tool can be used to check properties settings which describes modem' properties.
+; Generally, customization entries are added here which override commands from the All section
+
+[ModemX.AddReg]
+HKR,,Properties, 1, 80,01,00,00, ff,00,00,00, ff,00,00,00, 07,00,00,00, 0f,00,00,00, f7,03,00,00, 00,c2,01,00, 40,38,00,00
+HKR,,InactivityScale, 1, 0a,00,00,00
+HKR, Settings, InactivityTimeout,, "S30=<#>"
+HKR, Settings, ErrorControl_On,, "\N3"
+HKR, Settings, ErrorControl_Forced,, "\N2"
+HKR, Settings, Compression_On,, "%%C3"
+HKR, Settings, SpeedNegotiation_Off,, "N0"
+HKR, Settings, SpeedNegotiation_On,, "N1"
+
+;--------------------------------------------------------------------------------------------------------------------------
+;This section will define the type of modem and thus present the appropriate icon in the Device manager
+
+[INTERNAL]
+HKR,, DeviceType, 1, 02
+
+;-------------------------------------------------------------------------------------------------------------------------
+; Responses section contains all of the appropriate and required responses in order for the O/S to understand the
+; Modem responses to system queries.
+
+[All]
+HKR,,ConfigDialog,,modemui.dll
+HKR,,EnumPropPages,,"modemui.dll,EnumPropPages"
+HKR,,PortSubClass,1,02
+HKR,Init,1,,"AT<cr>"
+HKR,Init, 2,, "AT &F E0 V1 &D2 &C1 S0=0 W2 S95=47<cr>"
+
+[MfgAddReg]
+HKR,, InactivityScale, 1, 01,00,00,00
+HKR, Monitor, 1,, "ATS0=0<cr>"
+HKR, Monitor, 2,, "None"
+HKR, Hangup, 1,, "ATH<cr>"
+HKR, Answer, 1,, "ATA<cr>"
+HKR,, Reset,, "ATZ<cr>"
+HKR, Settings, Prefix,, "AT"
+HKR, Settings, Terminator,, "<cr>"
+HKR, Settings, DialPrefix,, "D"
+HKR, Settings, DialSuffix,, ";"
+HKR, Settings, SpeakerVolume_Low,, "L1"
+HKR, Settings, SpeakerVolume_Med,, "L2"
+HKR, Settings, SpeakerVolume_High,, "L3"
+HKR, Settings, SpeakerMode_Off,, "M0"
+HKR, Settings, SpeakerMode_Dial,, "M1"
+HKR, Settings, SpeakerMode_On,, "M2"
+HKR, Settings, SpeakerMode_Setup,, "M3"
+HKR, Settings, FlowControl_Off,, "&K0"
+HKR, Settings, FlowControl_Hard,, "&K3"
+HKR, Settings, FlowControl_Soft,, "&K4"
+HKR, Settings, ErrorControl_On,, "\N5"
+HKR, Settings, ErrorControl_Off,, "\N0"
+HKR, Settings, ErrorControl_Forced,, "\N4"
+HKR, Settings, Compression_On,, "%%C1"
+HKR, Settings, Compression_Off,, "%%C0"
+HKR, Settings, Modulation_CCITT,, "B0 "
+HKR, Settings, Modulation_Bell,, "B1 "
+HKR, Settings, SpeedNegotiation_Off,, "*S0"
+HKR, Settings, SpeedNegotiation_On,, "*S1"
+HKR, Settings, Pulse,, "P"
+HKR, Settings, Tone,, "T"
+HKR, Settings, Blind_Off,, "X4"
+HKR, Settings, Blind_On,, "X3"
+HKR, Settings, CallSetupFailTimer,, "S7=<#>"
+
+HKR, Responses, "<cr><lf>+FCERROR<cr><lf>", 1, 03, 00, 00,00,00,00, 00,00,00,00
+HKR, Responses, "<cr><lf>BLACKLISTED<cr><lf>", 1, 03, 00, 00,00,00,00, 00,00,00,00
+HKR, Responses, "<cr><lf>BUSY<cr><lf>", 1, 06, 00, 00,00,00,00, 00,00,00,00
+
+HKR, Responses, "<cr><lf>CONNECT<cr><lf>", 1, 02, 00, 00,00,00,00, 00,00,00,00
+HKR, Responses, "<cr><lf>DATA<cr><lf>", 1, 03, 00, 00,00,00,00, 00,00,00,00
+HKR, Responses, "<cr><lf>DELAYED<cr><lf>", 1, 03, 00, 00,00,00,00, 00,00,00,00
+HKR, Responses, "<cr><lf>ERROR<cr><lf>", 1, 03, 00, 00,00,00,00, 00,00,00,00
+
+HKR, Responses, "<cr><lf>FAX<cr><lf>", 1, 03, 00, 00,00,00,00, 00,00,00,00
+HKR, Responses, "<cr><lf>NO ANSWER<cr><lf>", 1, 07, 00, 00,00,00,00, 00,00,00,00
+HKR, Responses, "<cr><lf>NO CARRIER<cr><lf>", 1, 04, 00, 00,00,00,00, 00,00,00,00
+HKR, Responses, "<cr><lf>NO DIALTONE<cr><lf>", 1, 05, 00, 00,00,00,00, 00,00,00,00
+HKR, Responses, "<cr><lf>NOTUSED<cr><lf>", 1, 00, 00, 00,00,00,00, 00,00,00,00
+HKR, Responses, "<cr><lf>OK<cr><lf>", 1, 00, 00, 00,00,00,00, 00,00,00,00
+HKR, Responses, "<cr><lf>RING<cr><lf>", 1, 08, 00, 00,00,00,00, 00,00,00,00
+HKR, Responses, "<cr><lf>RINGING<cr><lf>", 1, 01, 00, 00,00,00,00, 00,00,00,00
+HKR, Responses, "0<cr>", 1, 00, 00, 00,00,00,00, 00,00,00,00 ; OK
+HKR, Responses, "1<cr>", 1, 02, 00, 00,00,00,00, 00,00,00,00
+HKR, Responses, "10<cr>", 1, 02, 00, 60,09,00,00, 00,00,00,00
+HKR, Responses, "11<cr>", 1, 02, 00, c0,12,00,00, 00,00,00,00
+HKR, Responses, "12<cr>", 1, 02, 00, 80,25,00,00, 00,00,00,00
+HKR, Responses, "13<cr>", 1, 02, 00, 20,1c,00,00, 00,00,00,00
+HKR, Responses, "14<cr>", 1, 02, 00, e0,2e,00,00, 00,00,00,00
+HKR, Responses, "15<cr>", 1, 02, 00, 40,38,00,00, 00,00,00,00 ; connect at 14400
+HKR, Responses, "2<cr>", 1, 08, 00, 00,00,00,00, 00,00,00,00 ; RING
+HKR, Responses, "3<cr>", 1, 04, 00, 00,00,00,00, 00,00,00,00 ; NO CARRIER
+HKR, Responses, "4<cr>", 1, 03, 00, 00,00,00,00, 00,00,00,00 ; ERROR
+HKR, Responses, "5<cr>", 1, 02, 00, b0,04,00,00, 00,00,00,00
+HKR, Responses, "6<cr>", 1, 05, 00, 00,00,00,00, 00,00,00,00 ; NO DIALTONE
+HKR, Responses, "69<cr>", 1, 01, 03, 00,00,00,00, 00,00,00,00
+HKR, Responses, "7<cr>", 1, 06, 00, 00,00,00,00, 00,00,00,00 ; BUSY
+HKR, Responses, "70<cr>", 1, 01, 01, 00,00,00,00, 00,00,00,00 ; Protocol:None
+HKR, Responses, "77<cr>", 1, 01, 02, 00,00,00,00, 00,00,00,00 ; Protocol:LAPM
+HKR, Responses, "78<cr>", 1, 01, 03, 00,00,00,00, 00,00,00,00 ; Connect V42BIS
+HKR, Responses, "8<cr>", 1, 07, 00, 00,00,00,00, 00,00,00,00 ; NO ANSWER
+HKR, Responses, "80<cr>", 1, 01, 02, 00,00,00,00, 00,00,00,00 ; Protocol:ALT
+HKR, Responses, "81<cr>", 1, 01, 02, 00,00,00,00, 00,00,00,00
+
+[ExtraCRLFResponses]
+HKR, Responses, "<cr><lf><cr><lf>OK<cr><lf>", 1, 00, 00, 00,00,00,00, 00,00,00,00
+HKR, Responses, "<cr><lf><cr><lf>ERROR<cr><lf>", 1, 03, 00, 00,00,00,00, 00,00,00,00
+HKR, Responses, "<cr><lf><cr><lf>BUSY<cr><lf>", 1, 06, 00, 00,00,00,00, 00,00,00,00
+HKR, Responses, "<cr><lf><cr><lf>NO ANSWER<cr><lf>", 1, 07, 00, 00,00,00,00, 00,00,00,00
+HKR, Responses, "<cr><lf><cr><lf>NO CARRIER<cr><lf>", 1, 04, 00, 00,00,00,00, 00,00,00,00
+HKR, Responses, "<cr><lf><cr><lf>NO DIALTONE<cr><lf>", 1, 05, 00, 00,00,00,00, 00,00,00,00
+
+
+; =================== Generic ==================================
+
+[Strings]
+MSFTUMDF="Microsoft"
+MediaDescription="Microsoft Sample Driver Installation Media"
+WudfRdDisplayName="Windows Driver Foundation - User-mode Driver Framework Reflector"
+FakeModemDeviceName="Microsoft Fake Modem User-Mode Device Sample"
diff --git a/serial/VirtualSerial/FakeModem/fakemodem.rc b/serial/VirtualSerial/FakeModem/fakemodem.rc
new file mode 100644
index 00000000..6bb6ed96
--- /dev/null
+++ b/serial/VirtualSerial/FakeModem/fakemodem.rc
@@ -0,0 +1,17 @@
+//---------------------------------------------------------------------------
+// FakeModem.rc
+//
+// Copyright (c) Microsoft Corporation, All Rights Reserved
+//---------------------------------------------------------------------------
+
+
+#include <windows.h>
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_DLL
+#define VER_FILESUBTYPE VFT_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "WDF:UMDF Fake Modem User-Mode Driver Sample"
+#define VER_INTERNALNAME_STR "FakeModem"
+#define VER_ORIGINALFILENAME_STR "FakeModem.dll"
+
+#include "common.ver"
diff --git a/serial/VirtualSerial/FakeModem/fakemodem.vcxproj b/serial/VirtualSerial/FakeModem/fakemodem.vcxproj
new file mode 100644
index 00000000..0eb999de
--- /dev/null
+++ b/serial/VirtualSerial/FakeModem/fakemodem.vcxproj
@@ -0,0 +1,256 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{8C65DE8E-4978-4EC0-8C6B-3C8BEF8FE281}</ProjectGuid>
+ <RootNamespace>$(MSBuildProjectName)</RootNamespace>
+ <UMDF_VERSION_MAJOR>1</UMDF_VERSION_MAJOR>
+ <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration>
+ <Platform Condition="'$(Platform)' == ''">Win32</Platform>
+ <SampleGuid>{BDA85DC8-F904-4FBD-B19D-160DD1CF4C3F}</SampleGuid>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>False</UseDebugLibraries>
+ <DriverTargetPlatform>Desktop</DriverTargetPlatform>
+ <DriverType>UMDF</DriverType>
+ <PlatformToolset>WindowsUserModeDriver10.0</PlatformToolset>
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>True</UseDebugLibraries>
+ <DriverTargetPlatform>Desktop</DriverTargetPlatform>
+ <DriverType>UMDF</DriverType>
+ <PlatformToolset>WindowsUserModeDriver10.0</PlatformToolset>
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>False</UseDebugLibraries>
+ <DriverTargetPlatform>Desktop</DriverTargetPlatform>
+ <DriverType>UMDF</DriverType>
+ <PlatformToolset>WindowsUserModeDriver10.0</PlatformToolset>
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>True</UseDebugLibraries>
+ <DriverTargetPlatform>Desktop</DriverTargetPlatform>
+ <DriverType>UMDF</DriverType>
+ <PlatformToolset>WindowsUserModeDriver10.0</PlatformToolset>
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <PropertyGroup>
+ <OutDir>$(IntDir)</OutDir>
+ </PropertyGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ItemGroup Label="WrappedTaskItems">
+ <ClCompile Include="..\dllsup.cpp; ..\comsup.cpp; ..\driver.cpp; ..\device.cpp; ..\queue.cpp; ..\ringbuffer.cpp">
+ <WppEnabled>true</WppEnabled>
+ <WppDllMacro>true</WppDllMacro>
+ <WppScanConfigurationData>..\internal.h</WppScanConfigurationData>
+ </ClCompile>
+ <Inf Include="fakemodem.inx">
+ <Architecture>$(InfArch)</Architecture>
+ <SpecifyArchitecture>true</SpecifyArchitecture>
+ <CopyOutput>.\$(IntDir)\fakemodem.inf</CopyOutput>
+ </Inf>
+ <OtherWpp Include="fakemodem.rc">
+ <WppEnabled>true</WppEnabled>
+ <WppDllMacro>true</WppDllMacro>
+ <WppScanConfigurationData>..\internal.h</WppScanConfigurationData>
+ </OtherWpp>
+ </ItemGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <TargetName>fakemodem</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <TargetName>fakemodem</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <TargetName>fakemodem</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <TargetName>fakemodem</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);_FAKE_MODEM=1;_UNICODE;UNICODE</PreprocessorDefinitions>
+ </ClCompile>
+ <Midl>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);_FAKE_MODEM=1;_UNICODE;UNICODE</PreprocessorDefinitions>
+ </Midl>
+ <ResourceCompile>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);_FAKE_MODEM=1;_UNICODE;UNICODE</PreprocessorDefinitions>
+ </ResourceCompile>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);_FAKE_MODEM=1;_UNICODE;UNICODE</PreprocessorDefinitions>
+ </ClCompile>
+ <Midl>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);_FAKE_MODEM=1;_UNICODE;UNICODE</PreprocessorDefinitions>
+ </Midl>
+ <ResourceCompile>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);_FAKE_MODEM=1;_UNICODE;UNICODE</PreprocessorDefinitions>
+ </ResourceCompile>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);_FAKE_MODEM=1;_UNICODE;UNICODE</PreprocessorDefinitions>
+ </ClCompile>
+ <Midl>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);_FAKE_MODEM=1;_UNICODE;UNICODE</PreprocessorDefinitions>
+ </Midl>
+ <ResourceCompile>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);_FAKE_MODEM=1;_UNICODE;UNICODE</PreprocessorDefinitions>
+ </ResourceCompile>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);_FAKE_MODEM=1;_UNICODE;UNICODE</PreprocessorDefinitions>
+ </ClCompile>
+ <Midl>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);_FAKE_MODEM=1;_UNICODE;UNICODE</PreprocessorDefinitions>
+ </Midl>
+ <ResourceCompile>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);_FAKE_MODEM=1;_UNICODE;UNICODE</PreprocessorDefinitions>
+ </ResourceCompile>
+ </ItemDefinitionGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <WIN32_WINNT_VERSION>0x0A00</WIN32_WINNT_VERSION>
+ <NTDDI_VERSION>0x0A000000</NTDDI_VERSION>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <WIN32_WINNT_VERSION>0x0A00</WIN32_WINNT_VERSION>
+ <NTDDI_VERSION>0x0A000000</NTDDI_VERSION>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <WIN32_WINNT_VERSION>0x0A00</WIN32_WINNT_VERSION>
+ <NTDDI_VERSION>0x0A000000</NTDDI_VERSION>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <WIN32_WINNT_VERSION>0x0A00</WIN32_WINNT_VERSION>
+ <NTDDI_VERSION>0x0A000000</NTDDI_VERSION>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Link>
+ <EntryPointSymbol Condition="'$(Platform)'=='win32'">_DllMainCRTStartup@12</EntryPointSymbol>
+ <EntryPointSymbol Condition="'$(Platform)'!='win32'">_DllMainCRTStartup</EntryPointSymbol>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Link>
+ <EntryPointSymbol Condition="'$(Platform)'=='win32'">_DllMainCRTStartup@12</EntryPointSymbol>
+ <EntryPointSymbol Condition="'$(Platform)'!='win32'">_DllMainCRTStartup</EntryPointSymbol>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Link>
+ <EntryPointSymbol Condition="'$(Platform)'=='win32'">_DllMainCRTStartup@12</EntryPointSymbol>
+ <EntryPointSymbol Condition="'$(Platform)'!='win32'">_DllMainCRTStartup</EntryPointSymbol>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Link>
+ <EntryPointSymbol Condition="'$(Platform)'=='win32'">_DllMainCRTStartup@12</EntryPointSymbol>
+ <EntryPointSymbol Condition="'$(Platform)'!='win32'">_DllMainCRTStartup</EntryPointSymbol>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <WarningLevel>Level4</WarningLevel>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\strsafe.lib;$(SDK_LIB_PATH)\ntdll.lib;$(SDK_LIB_PATH)\kernel32.lib;$(SDK_LIB_PATH)\advapi32.lib;$(SDK_LIB_PATH)\Ole32.lib;$(SDK_LIB_PATH)\setupapi.lib</AdditionalDependencies>
+ <ModuleDefinitionFile>exports.def</ModuleDefinitionFile>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <WarningLevel>Level4</WarningLevel>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\strsafe.lib;$(SDK_LIB_PATH)\ntdll.lib;$(SDK_LIB_PATH)\kernel32.lib;$(SDK_LIB_PATH)\advapi32.lib;$(SDK_LIB_PATH)\Ole32.lib;$(SDK_LIB_PATH)\setupapi.lib</AdditionalDependencies>
+ <ModuleDefinitionFile>exports.def</ModuleDefinitionFile>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <WarningLevel>Level4</WarningLevel>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\strsafe.lib;$(SDK_LIB_PATH)\ntdll.lib;$(SDK_LIB_PATH)\kernel32.lib;$(SDK_LIB_PATH)\advapi32.lib;$(SDK_LIB_PATH)\Ole32.lib;$(SDK_LIB_PATH)\setupapi.lib</AdditionalDependencies>
+ <ModuleDefinitionFile>exports.def</ModuleDefinitionFile>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <WarningLevel>Level4</WarningLevel>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\strsafe.lib;$(SDK_LIB_PATH)\ntdll.lib;$(SDK_LIB_PATH)\kernel32.lib;$(SDK_LIB_PATH)\advapi32.lib;$(SDK_LIB_PATH)\Ole32.lib;$(SDK_LIB_PATH)\setupapi.lib</AdditionalDependencies>
+ <ModuleDefinitionFile>exports.def</ModuleDefinitionFile>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ResourceCompile Include="fakemodem.rc" />
+ </ItemGroup>
+ <ItemGroup>
+ <Inf Exclude="@(Inf)" Include="*.inf" />
+ <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" />
+ <FilesToPackage Include="@(Inf->'%(CopyOutput)')" Condition="'@(Inf)'!=''" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Exclude="@(None)" Include="*.txt;*.htm;*.html" />
+ <None Exclude="@(None)" Include="*.ico;*.cur;*.bmp;*.dlg;*.rct;*.gif;*.jpg;*.jpeg;*.wav;*.jpe;*.tiff;*.tif;*.png;*.rc2" />
+ <None Exclude="@(None)" Include="*.def;*.bat;*.hpj;*.asmx" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+</Project> \ No newline at end of file
diff --git a/serial/VirtualSerial/FakeModem/fakemodem.vcxproj.Filters b/serial/VirtualSerial/FakeModem/fakemodem.vcxproj.Filters
new file mode 100644
index 00000000..1471427c
--- /dev/null
+++ b/serial/VirtualSerial/FakeModem/fakemodem.vcxproj.Filters
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions>
+ <UniqueIdentifier>{171E6CE6-A3BC-4E0C-A70B-3D3A14E3013E}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files">
+ <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
+ <UniqueIdentifier>{7D188E14-FC54-4649-BF86-227A339DF62D}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Resource Files">
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions>
+ <UniqueIdentifier>{376804A3-5F06-4C4D-8E78-89D50B462EA5}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Driver Files">
+ <Extensions>inf;inv;inx;mof;mc;</Extensions>
+ <UniqueIdentifier>{CD2DEFBB-4830-410C-8909-649347BC8898}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\comsup.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\device.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\dllsup.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\driver.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\queue.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\ringbuffer.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <None Include="exports.def">
+ <Filter>Source Files</Filter>
+ </None>
+ </ItemGroup>
+ <ItemGroup>
+ <FilesToPackage Include=".\Debug\\fakemodem.inf">
+ <Filter>Driver Files</Filter>
+ </FilesToPackage>
+ <Inf Include="fakemodem.inx">
+ <Filter>Driver Files</Filter>
+ </Inf>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="fakemodem.rc">
+ <Filter>Resource Files</Filter>
+ </ResourceCompile>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/serial/VirtualSerial/ReadMe.md b/serial/VirtualSerial/ReadMe.md
new file mode 100644
index 00000000..2b1bc513
--- /dev/null
+++ b/serial/VirtualSerial/ReadMe.md
@@ -0,0 +1,59 @@
+Virtual serial driver sample
+============================
+
+This sample demonstrates these two serial drivers:
+
+- A simple virtual serial driver (ComPort)
+- A controller-less modem driver (FakeModem).This driver supports sending and receiving AT commands using the ReadFile and WriteFile calls or via a TAPI interface using an application such as, HyperTerminal.
+
+This sample driver is a minimal driver meant to demonstrate the usage of the User-Mode Driver Framework. It is not intended for use in a production environment.
+
+For more information, see [Serial Controller and Device Drivers](http://msdn.microsoft.com/en-us/library/windows/hardware/ff546939) in the WDK documentation.
+
+Code tour
+---------
+
+File manifest
+
+Description
+
+comsup.cpp & comsup.h
+
+COM Support code - specifically base classes which provide implementations for the standard COM interfaces **IUnknown** and **IClassFactory** which are used throughout 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
+
+DLL Support code - provides the DLL's entry point as well as the single required export (**DllGetClassObject**).
+
+These depend on comsup.cpp to perform the necessary class creation.
+
+exports.def
+
+This file lists the functions that the driver DLL exports.
+
+internal.h
+
+This is the main header file for the sample driver.
+
+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
+
+Definition and implementation of the device callback class (CMyDriver) for the sample. This includes events on the framework device object.
+
+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
+
+This file defines resource information for the sample driver.
+
+VirtualSerial.inf / FakeModem.inf
+
+INF file that contains installation information for this driver.
+
diff --git a/serial/VirtualSerial/VirtualSerial.sln b/serial/VirtualSerial/VirtualSerial.sln
new file mode 100644
index 00000000..e682dae3
--- /dev/null
+++ b/serial/VirtualSerial/VirtualSerial.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}") = "ComPort", "ComPort", "{6C0EE406-586D-4B80-BB5D-DAF3113ECF24}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "FakeModem", "FakeModem", "{E25B6521-5167-4BB2-B779-8C56A9B9B980}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "virtualserial", "ComPort\virtualserial.vcxproj", "{EFB282A8-A881-44E7-964D-F068A4FD19C9}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fakemodem", "FakeModem\fakemodem.vcxproj", "{8C65DE8E-4978-4EC0-8C6B-3C8BEF8FE281}"
+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
+ {EFB282A8-A881-44E7-964D-F068A4FD19C9}.Debug|Win32.ActiveCfg = Debug|Win32
+ {EFB282A8-A881-44E7-964D-F068A4FD19C9}.Debug|Win32.Build.0 = Debug|Win32
+ {EFB282A8-A881-44E7-964D-F068A4FD19C9}.Release|Win32.ActiveCfg = Release|Win32
+ {EFB282A8-A881-44E7-964D-F068A4FD19C9}.Release|Win32.Build.0 = Release|Win32
+ {EFB282A8-A881-44E7-964D-F068A4FD19C9}.Debug|x64.ActiveCfg = Debug|x64
+ {EFB282A8-A881-44E7-964D-F068A4FD19C9}.Debug|x64.Build.0 = Debug|x64
+ {EFB282A8-A881-44E7-964D-F068A4FD19C9}.Release|x64.ActiveCfg = Release|x64
+ {EFB282A8-A881-44E7-964D-F068A4FD19C9}.Release|x64.Build.0 = Release|x64
+ {8C65DE8E-4978-4EC0-8C6B-3C8BEF8FE281}.Debug|Win32.ActiveCfg = Debug|Win32
+ {8C65DE8E-4978-4EC0-8C6B-3C8BEF8FE281}.Debug|Win32.Build.0 = Debug|Win32
+ {8C65DE8E-4978-4EC0-8C6B-3C8BEF8FE281}.Release|Win32.ActiveCfg = Release|Win32
+ {8C65DE8E-4978-4EC0-8C6B-3C8BEF8FE281}.Release|Win32.Build.0 = Release|Win32
+ {8C65DE8E-4978-4EC0-8C6B-3C8BEF8FE281}.Debug|x64.ActiveCfg = Debug|x64
+ {8C65DE8E-4978-4EC0-8C6B-3C8BEF8FE281}.Debug|x64.Build.0 = Debug|x64
+ {8C65DE8E-4978-4EC0-8C6B-3C8BEF8FE281}.Release|x64.ActiveCfg = Release|x64
+ {8C65DE8E-4978-4EC0-8C6B-3C8BEF8FE281}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {EFB282A8-A881-44E7-964D-F068A4FD19C9} = {6C0EE406-586D-4B80-BB5D-DAF3113ECF24}
+ {8C65DE8E-4978-4EC0-8C6B-3C8BEF8FE281} = {E25B6521-5167-4BB2-B779-8C56A9B9B980}
+ EndGlobalSection
+EndGlobal
diff --git a/serial/VirtualSerial/comsup.cpp b/serial/VirtualSerial/comsup.cpp
new file mode 100644
index 00000000..fd298470
--- /dev/null
+++ b/serial/VirtualSerial/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<IUnknown *>(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<IClassFactory *>(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/serial/VirtualSerial/comsup.h b/serial/VirtualSerial/comsup.h
new file mode 100644
index 00000000..a73938b6
--- /dev/null
+++ b/serial/VirtualSerial/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/serial/VirtualSerial/device.cpp b/serial/VirtualSerial/device.cpp
new file mode 100644
index 00000000..11d47775
--- /dev/null
+++ b/serial/VirtualSerial/device.cpp
@@ -0,0 +1,674 @@
+/*++
+
+Copyright (C) Microsoft Corporation, All Rights Reserved.
+
+Module Name:
+
+ Device.cpp
+
+Abstract:
+
+ This module contains the implementation of the UMDF VirtualSerial sample
+ driver's device callback object.
+
+ The VirtualSerial sample device does very little. It does not implement
+ either of the PNP interfaces so once the device is setup, it won't ever get
+ any callbacks until the device is removed.
+
+Environment:
+
+ Windows User-Mode Driver Framework (WUDF)
+
+--*/
+
+#include "internal.h"
+#include "device.tmh"
+
+HRESULT
+CMyDevice::CreateInstance(
+ _In_ IWDFDriver *FxDriver,
+ _In_ IWDFDeviceInitialize * FxDeviceInit,
+ _Out_ PCMyDevice *Device
+ )
+/*++
+
+ Routine Description:
+
+ This method creates and initializs an instance of the VirtualSerial 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.
+ //
+ FxDeviceInit->SetLockingConstraint(WdfDeviceLevel);
+
+ //
+ // Create a new FX device object and assign the new callback object to
+ // handle any device level events that occur.
+ //
+
+ //
+ // QueryIUnknown references the IUnknown interface that it returns
+ // (which is the same as referencing the device). We pass that to
+ // CreateDevice, which takes its own reference if everything works.
+ //
+
+ {
+ IUnknown *unknown = this->QueryIUnknown();
+
+ hr = FxDriver->CreateDevice(FxDeviceInit, unknown, &fxDevice);
+
+ unknown->Release();
+ }
+
+ //
+ // If that succeeded then set our FxDevice member variable.
+ //
+
+ if (SUCCEEDED(hr))
+ {
+ m_FxDevice = fxDevice;
+
+ //
+ // Drop the reference we got from CreateDevice. Since this object
+ // is partnered with the framework object they have the same
+ // lifespan - there is no need for an additional reference.
+ //
+
+ fxDevice->Release();
+ }
+
+ return hr;
+}
+
+HRESULT
+CMyDevice::Configure(
+ VOID
+ )
+/*++
+
+ Routine Description:
+
+ This method is called after the device callback object has been initialized
+ and returned to the driver. It would setup the device's queues and their
+ corresponding callback objects.
+
+ Arguments:
+
+ FxDevice - the framework device object for which we're handling events.
+
+ Return Value:
+
+ status
+
+--*/
+{
+ IWDFPropertyStoreFactory *pPropertyStoreFactory = NULL;
+ IWDFNamedPropertyStore2 * pHardwarePropertyStore = NULL;
+ IWDFNamedPropertyStore2 * pLegacyHardwarePropertyStore = NULL;
+ WDF_PROPERTY_STORE_ROOT RootSpecifier;
+ PROPVARIANT comPortPV;
+ WCHAR portName[] = L"PortName";
+ size_t comPortSuffixCch = 0;
+ WCHAR *comPortFullName = NULL;
+ size_t comPortFullNameCch = 0;
+ WCHAR *pdoName = NULL;
+ PCMyQueue defaultQueue;
+
+#ifdef _FAKE_MODEM
+ //
+ // Compiled as fake modem
+ //
+ LPGUID pGuid = (LPGUID) &GUID_DEVINTERFACE_MODEM;
+#else
+ //
+ // Compiled as virtual serial port
+ //
+ LPGUID pGuid = (LPGUID) &GUID_DEVINTERFACE_COMPORT;
+#endif
+
+ HRESULT hr;
+
+ PropVariantInit(&comPortPV);
+
+ //
+ // Create device interface
+ //
+ hr = m_FxDevice->CreateDeviceInterface(pGuid,
+ NULL);
+
+ if (FAILED(hr))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ L"ERROR: Cannot create device interface (%!GUID!)",
+ pGuid
+ );
+
+ goto Exit;
+ }
+
+ hr = m_FxDevice->AssignDeviceInterfaceState(pGuid,
+ NULL,
+ TRUE);
+
+ if (FAILED(hr))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ L"ERROR: Cannot enable device interface (%!GUID!)",
+ pGuid
+ );
+
+ goto Exit;
+ }
+
+ //
+ // Create Symbolic Link
+ //
+
+ //
+ // First we need to read the COM number from the registry
+ //
+
+ hr = m_FxDevice->QueryInterface(IID_PPV_ARGS(&pPropertyStoreFactory));
+
+ if (FAILED(hr))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ L"ERROR: QI for IWDFPropertyStoreFactory failed"
+ );
+
+ goto Exit;
+ }
+
+ RootSpecifier.LengthCb = sizeof(WDF_PROPERTY_STORE_ROOT);
+ RootSpecifier.RootClass = WdfPropertyStoreRootClassHardwareKey;
+ RootSpecifier.Qualifier.HardwareKey.ServiceName =
+ WDF_PROPERTY_STORE_HARDWARE_KEY_ROOT;
+
+ hr = pPropertyStoreFactory->RetrieveDevicePropertyStore(
+ &RootSpecifier,
+ WdfPropertyStoreNormal,
+ KEY_QUERY_VALUE,
+ NULL,
+ &pHardwarePropertyStore,
+ NULL
+ );
+
+ if (FAILED(hr))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ L"ERROR: Failed to retrieve device hardware key root"
+ );
+ goto Exit;
+ }
+
+ hr = pHardwarePropertyStore->GetNamedValue(portName, &comPortPV);
+
+ if (FAILED(hr))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ L"ERROR: Failed to read value %ws",
+ portName
+ );
+ goto Exit;
+ }
+
+ //
+ // Then we need to construct the COM port name
+ //
+ hr = StringCchLength(comPortPV.pwszVal,
+ STRSAFE_MAX_CCH,
+ &comPortSuffixCch);
+
+ if (FAILED(hr))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ L"ERROR: Overflow while calculating COM port suffix length"
+ );
+ goto Exit;
+ }
+
+ hr = SizeTAdd(ARRAYSIZE(SYMBOLIC_LINK_NAME_PREFIX),
+ comPortSuffixCch,
+ &comPortFullNameCch);
+
+ if (FAILED(hr))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ L"ERROR: Overflow while calculating symbolic link length"
+ );
+ goto Exit;
+ }
+
+ comPortFullName = (WCHAR*) new WCHAR[comPortFullNameCch];
+
+ if (NULL == comPortFullName)
+ {
+ hr = E_OUTOFMEMORY;
+ Trace(
+ TRACE_LEVEL_ERROR,
+ L"ERROR: Unable to allocate memory for full port name"
+ );
+ goto Exit;
+ }
+
+ hr = StringCchPrintf(comPortFullName,
+ comPortFullNameCch,
+ L"%ws%ws",
+ SYMBOLIC_LINK_NAME_PREFIX,
+ comPortPV.pwszVal);
+
+ if (FAILED(hr))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ L"ERROR: Cannot create full name for COM port"
+ );
+ goto Exit;
+ }
+
+ //
+ // Finally we create the symbolic link
+ //
+ hr = m_FxDevice->CreateSymbolicLink(comPortFullName);
+
+ if (FAILED(hr))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ L"ERROR: Cannot create symbolic link %ws",
+ comPortFullName
+ );
+ goto Exit;
+ }
+
+ //
+ // Write the com name to the legacy hardware key
+ //
+ hr = GetPdoName(&pdoName);
+
+ if (FAILED(hr) || (pdoName == NULL))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ L"ERROR: Cannot retrieve PDO name"
+ );
+ goto Exit;
+ }
+
+ //
+ // Write the name to the legacy hardware key
+ //
+ RootSpecifier.LengthCb = sizeof(WDF_PROPERTY_STORE_ROOT);
+ RootSpecifier.RootClass = WdfPropertyStoreRootClassLegacyHardwareKey;
+ RootSpecifier.Qualifier.LegacyHardwareKey.LegacyMapName = L"SERIALCOMM";
+
+ hr = pPropertyStoreFactory->RetrieveDevicePropertyStore(
+ &RootSpecifier,
+ WdfPropertyStoreCreateVolatile,
+ KEY_SET_VALUE,
+ NULL,
+ &pLegacyHardwarePropertyStore,
+ NULL
+ );
+
+ if (FAILED(hr))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ L"ERROR: Failed to retrieve device legacy hardware key"
+ );
+ goto Exit;
+ }
+
+ hr = pLegacyHardwarePropertyStore->SetNamedValue(pdoName,
+ &comPortPV);
+
+ if (FAILED(hr))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ L"ERROR: Failed to write device name to legacy hardware key"
+ );
+ goto Exit;
+ }
+
+ m_CreatedLegacyHardwareKey = TRUE;
+ m_LegacyHardwarePropertyStore = pLegacyHardwarePropertyStore;
+ m_PdoName = pdoName;
+
+ //
+ // Create and configure the queues
+ //
+ hr = CMyQueue::CreateInstance(this, m_FxDevice, &defaultQueue);
+
+ if (FAILED(hr))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ L"ERROR: Failed to create default queue"
+ );
+ goto Exit;
+ }
+
+ defaultQueue->Release();
+
+
+Exit:
+
+ if (m_CreatedLegacyHardwareKey == FALSE)
+ {
+ //
+ // If the legacy hardware key has been written, then the cleanup
+ // will happen, when the device is unloaded
+ //
+ SAFE_RELEASE(pLegacyHardwarePropertyStore);
+ if (pdoName != NULL)
+ {
+ delete[] pdoName;
+ }
+ }
+
+ SAFE_RELEASE(pHardwarePropertyStore);
+ SAFE_RELEASE(pPropertyStoreFactory);
+
+ PropVariantClear(&comPortPV);
+
+ if (comPortFullName != NULL)
+ {
+ delete[] comPortFullName;
+ }
+
+ 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(IObjectCleanup)))
+ {
+ *Object = QueryIObjectCleanup();
+ hr = S_OK;
+ }
+ else
+ {
+ hr = CUnknown::QueryInterface(InterfaceId, Object);
+ }
+
+ return hr;
+}
+
+VOID
+CMyDevice::OnCleanup(
+ IWDFObject* pWdfObject
+)
+{
+ UNREFERENCED_PARAMETER(pWdfObject);
+
+ if ((m_CreatedLegacyHardwareKey == TRUE) &&
+ (m_LegacyHardwarePropertyStore != NULL))
+ {
+ m_LegacyHardwarePropertyStore->DeleteNamedValue(m_PdoName);
+ SAFE_RELEASE(m_LegacyHardwarePropertyStore);
+ delete[] m_PdoName;
+ }
+}
+
+HRESULT
+CMyDevice::GetPdoName(
+ _Out_ LPWSTR *PdoName
+ )
+{
+ HRESULT hr = S_OK;
+ WCHAR *devInstId = NULL;
+ ULONG devInstIdLen;
+ HDEVINFO hDevInfoSet = INVALID_HANDLE_VALUE;
+ SP_DEVINFO_DATA devInfoData;
+ WCHAR *pdoName = NULL;
+ DWORD pdoNameCb = 0;
+
+ WUDF_TEST_DRIVER_ASSERT(m_FxDevice);
+
+ //
+ // First let's get the device instance ID
+ //
+ devInstIdLen = 0;
+ hr = m_FxDevice->RetrieveDeviceInstanceId(NULL, &devInstIdLen);
+ if (SUCCEEDED(hr))
+ {
+ devInstId = (WCHAR*) new WCHAR[devInstIdLen];
+ if (NULL == devInstId)
+ {
+ hr = E_OUTOFMEMORY;
+ }
+ }
+ if (SUCCEEDED(hr))
+ {
+ hr = m_FxDevice->RetrieveDeviceInstanceId(devInstId, &devInstIdLen);
+ }
+
+ //
+ // Now use the SetupDiXxx functions to get a handle to the device's
+ // hardware key
+ //
+ if (SUCCEEDED(hr))
+ {
+ //
+ // Create a new device information set
+ //
+ hDevInfoSet = SetupDiCreateDeviceInfoList(NULL, NULL);
+ if (INVALID_HANDLE_VALUE == hDevInfoSet)
+ {
+ hr = HRESULT_FROM_WIN32(GetLastError());
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ //
+ // Add our device to this device information set
+ //
+ ZeroMemory(&devInfoData, sizeof(devInfoData));
+ devInfoData.cbSize = sizeof(devInfoData);
+ if (!SetupDiOpenDeviceInfo(hDevInfoSet,
+ devInstId,
+ NULL,
+ 0,
+ &devInfoData))
+ {
+ hr = HRESULT_FROM_WIN32(GetLastError());
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ //
+ // Get the length of the PDO name
+ //
+ if (!SetupDiGetDeviceRegistryProperty(hDevInfoSet,
+ &devInfoData,
+ SPDRP_PHYSICAL_DEVICE_OBJECT_NAME,
+ NULL,
+ (PBYTE)pdoName,
+ 0,
+ &pdoNameCb))
+ {
+ //
+ // The only reason for this call is to get the length of the
+ // buffer. The only non acceptable reason for failure is, if we
+ // asked for invalid data.
+ //
+ if (GetLastError() == ERROR_INVALID_DATA)
+ {
+ hr = HRESULT_FROM_WIN32(GetLastError());
+ }
+ }
+ }
+
+ //
+ // Get the PDO name
+ //
+ if (SUCCEEDED(hr))
+ {
+ pdoName = (WCHAR *)new BYTE[pdoNameCb];
+
+ if (pdoName == NULL)
+ {
+ hr = E_OUTOFMEMORY;
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ if (!SetupDiGetDeviceRegistryProperty(hDevInfoSet,
+ &devInfoData,
+ SPDRP_PHYSICAL_DEVICE_OBJECT_NAME,
+ NULL,
+ (PBYTE)pdoName,
+ pdoNameCb,
+ NULL))
+ {
+ hr = HRESULT_FROM_WIN32(GetLastError());
+ }
+
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ *PdoName = pdoName;
+ }
+
+ //
+ // clean up to be done regardless of success or failure
+ //
+ if (NULL != devInstId)
+ {
+ delete[] devInstId;
+ }
+
+ if (INVALID_HANDLE_VALUE != hDevInfoSet)
+ {
+ SetupDiDestroyDeviceInfoList(hDevInfoSet);
+ }
+
+ //
+ // clean up to be done in case of failure only
+ //
+ if (FAILED(hr))
+ {
+ if (NULL != pdoName)
+ {
+ delete[] pdoName;
+ pdoName = NULL;
+ }
+ }
+
+ return hr;
+}
diff --git a/serial/VirtualSerial/device.h b/serial/VirtualSerial/device.h
new file mode 100644
index 00000000..1fc54aeb
--- /dev/null
+++ b/serial/VirtualSerial/device.h
@@ -0,0 +1,261 @@
+/*++
+
+Copyright (C) Microsoft Corporation, All Rights Reserved
+
+Module Name:
+
+ Device.h
+
+Abstract:
+
+ This module contains the type definitions for the UMDF VirtualSerial sample
+ driver's device callback class.
+
+Environment:
+
+ Windows User-Mode Driver Framework (WUDF)
+
+--*/
+
+#pragma once
+#include "initguid.h"
+#include "serial.h"
+
+DEFINE_GUID(GUID_DEVINTERFACE_MODEM, 0x2c7089aa, 0x2e0e, 0x11d1, 0xb1, 0x14, 0x00, 0xc0, 0x4f, 0xc2, 0xaa, 0xe4);
+DEFINE_GUID(GUID_DEVINTERFACE_COMPORT, 0x86e0d1e0L, 0x8089, 0x11d0, 0x9c, 0xe4, 0x08, 0x00, 0x3e, 0x30, 0x1f, 0x73);
+
+#define SYMBOLIC_LINK_NAME_PREFIX L"\\DosDevices\\Global\\"
+
+//
+// Class for the VirtualSerial driver.
+//
+
+class CMyDevice :
+ public CUnknown,
+ public IObjectCleanup
+{
+
+//
+// Private data members.
+//
+private:
+
+ IWDFDevice *m_FxDevice;
+
+ //
+ // Baud rate
+ //
+ ULONG m_BaudRate;
+
+ //
+ // Modem control register
+ //
+ ULONG m_MCR;
+
+ //
+ // FIFO control register
+ //
+ ULONG m_FCR;
+
+ //
+ // Line control register
+ //
+ ULONG m_LCR;
+
+ //
+ // Valid data mask
+ //
+ UCHAR m_ValidDataMask;
+
+ //
+ // Timeouts
+ //
+ SERIAL_TIMEOUTS m_Timeouts;
+
+ //
+ // Legacy hardware key property store
+ //
+ BOOL m_CreatedLegacyHardwareKey;
+
+ IWDFNamedPropertyStore2 * m_LegacyHardwarePropertyStore;
+
+ PWCHAR m_PdoName;
+//
+// Private methods.
+//
+
+private:
+
+ CMyDevice(
+ VOID
+ ) :
+ m_FxDevice(NULL),
+ m_BaudRate(0),
+ m_MCR(0),
+ m_FCR(0),
+ m_LCR(0),
+ m_ValidDataMask(0),
+ m_CreatedLegacyHardwareKey(FALSE),
+ m_LegacyHardwarePropertyStore(NULL),
+ m_PdoName(NULL)
+ {
+ ZeroMemory(&m_Timeouts, sizeof(SERIAL_TIMEOUTS));
+ }
+
+ HRESULT
+ Initialize(
+ _In_ IWDFDriver *FxDriver,
+ _In_ IWDFDeviceInitialize *FxDeviceInit
+ );
+
+ HRESULT
+ CMyDevice::GetPdoName(
+ _Out_ LPWSTR *PdoName
+ );
+//
+// 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
+ );
+
+ //
+ // IObjectCleanup
+ //
+ IObjectCleanup *
+ QueryIObjectCleanup(
+ VOID
+ )
+ {
+ AddRef();
+ return static_cast<IObjectCleanup *>(this);
+ }
+
+ virtual
+ void
+ STDMETHODCALLTYPE
+ OnCleanup(
+ IWDFObject* pWdfObject
+ );
+
+
+ ULONG
+ GetBaudRate(
+ VOID
+ )
+ {
+ return m_BaudRate;
+ }
+
+ void
+ SetBaudRate(
+ _In_ ULONG BaudRate
+ )
+ {
+ m_BaudRate = BaudRate;
+
+ return;
+ }
+
+ ULONG *
+ GetModemControlRegisterPtr(
+ VOID
+ )
+ {
+ return &m_MCR;
+ }
+
+ ULONG *
+ GetFifoControlRegisterPtr(
+ VOID
+ )
+ {
+ return &m_FCR;
+ }
+
+ ULONG *
+ GetLineControlRegisterPtr(
+ VOID
+ )
+ {
+ return &m_LCR;
+ }
+
+ VOID
+ SetValidDataMask(
+ _In_ UCHAR Mask
+ )
+ {
+ m_ValidDataMask = Mask;
+ }
+
+ VOID
+ SetTimeouts(
+ _In_ SERIAL_TIMEOUTS Timeouts
+ )
+ {
+ m_Timeouts = Timeouts;
+ }
+
+ VOID
+ GetTimeouts(
+ _Out_ SERIAL_TIMEOUTS *Timeouts
+ )
+ {
+ *Timeouts = m_Timeouts;;
+ }
+
+};
diff --git a/serial/VirtualSerial/dllsup.cpp b/serial/VirtualSerial/dllsup.cpp
new file mode 100644
index 00000000..6c254085
--- /dev/null
+++ b/serial/VirtualSerial/dllsup.cpp
@@ -0,0 +1,177 @@
+/*++
+
+Copyright (C) Microsoft Corporation, All Rights Reserved.
+
+Module Name:
+
+ dllsup.cpp
+
+Abstract:
+
+ This module contains the implementation of the UMDF VirtualSerial Sample
+ Driver's entry point and its exported functions for providing COM support.
+
+ This module can be copied without modification to a new UMDF driver. It
+ depends on some of the code in comsup.cpp & comsup.h to handle DLL
+ registration and creating the first class factory.
+
+ This module is dependent on the following defines:
+
+ MYDRIVER_TRACING_ID - A wide string passed to WPP when initializing
+ tracing. For example, VirtualSerial uses
+ L"Microsoft\\UMDF\\VirtualSerial"
+
+ MYDRIVER_CLASS_ID - A GUID encoded in struct format used to
+ initialize the driver's ClassID.
+
+ These are defined in internal.h for the sample. If you choose
+ to use a different primary include file, you should ensure they are
+ defined there as well.
+
+Environment:
+
+ WDF User-Mode Driver Framework (WDF:UMDF)
+
+--*/
+
+#include "internal.h"
+#include "dllsup.tmh"
+
+const GUID CLSID_MyDriverCoClass = MYDRIVER_CLASS_ID;
+
+BOOL
+WINAPI
+DllMain(
+ HINSTANCE ModuleHandle,
+ DWORD Reason,
+ PVOID /* Reserved */
+ )
+/*++
+
+ Routine Description:
+
+ This is the entry point and exit point for the I/O trace driver. This
+ does very little as the I/O trace driver has minimal global data.
+
+ This method initializes tracing.
+
+ Arguments:
+
+ ModuleHandle - the DLL handle for this module.
+
+ Reason - the reason this entry point was called.
+
+ Reserved - unused
+
+ Return Value:
+
+ TRUE
+
+--*/
+{
+
+ UNREFERENCED_PARAMETER( ModuleHandle );
+
+ if (DLL_PROCESS_ATTACH == Reason)
+ {
+ //
+ // Initialize tracing.
+ //
+
+ WPP_INIT_TRACING(MYDRIVER_TRACING_ID);
+
+ }
+ else if (DLL_PROCESS_DETACH == Reason)
+ {
+ //
+ // Cleanup tracing.
+ //
+
+ WPP_CLEANUP();
+ }
+
+ return TRUE;
+}
+
+HRESULT
+STDAPICALLTYPE
+DllGetClassObject(
+ _In_ REFCLSID ClassId,
+ _In_ REFIID InterfaceId,
+ _Outptr_ LPVOID *Interface
+ )
+/*++
+
+ Routine Description:
+
+ This routine is called by COM in order to instantiate the
+ driver callback object and do an initial query interface on it.
+
+ This method only creates an instance of the driver's class factory, as this
+ is the minimum required to support UMDF.
+
+ Arguments:
+
+ ClassId - the CLSID of the object being "gotten"
+
+ InterfaceId - the interface the caller wants from that object.
+
+ Interface - a location to store the referenced interface pointer
+
+ Return Value:
+
+ S_OK if the function succeeds or error indicating the cause of the
+ failure.
+
+--*/
+{
+ PCClassFactory factory;
+
+ HRESULT hr = S_OK;
+
+ *Interface = NULL;
+
+ //
+ // If the CLSID doesn't match that of our "coclass" (defined in the IDL
+ // file) then we can't create the object the caller wants. This may
+ // indicate that the COM registration is incorrect, and another CLSID
+ // is referencing this drvier.
+ //
+
+ if (IsEqualCLSID(ClassId, CLSID_MyDriverCoClass) == false)
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ L"ERROR: Called to create instance of unrecognized class (%!GUID!)",
+ &ClassId
+ );
+
+ return CLASS_E_CLASSNOTAVAILABLE;
+ }
+
+ //
+ // Create an instance of the class factory for the caller.
+ //
+
+ factory = new CClassFactory();
+
+ if (NULL == factory)
+ {
+ hr = E_OUTOFMEMORY;
+ }
+
+ //
+ // Query the object we created for the interface the caller wants. After
+ // that we release the object. This will drive the reference count to
+ // 1 (if the QI succeeded an referenced the object) or 0 (if the QI failed).
+ // In the later case the object is automatically deleted.
+ //
+
+ if (SUCCEEDED(hr))
+ {
+ hr = factory->QueryInterface(InterfaceId, Interface);
+ factory->Release();
+ }
+
+ return hr;
+}
diff --git a/serial/VirtualSerial/driver.cpp b/serial/VirtualSerial/driver.cpp
new file mode 100644
index 00000000..0ec28ec5
--- /dev/null
+++ b/serial/VirtualSerial/driver.cpp
@@ -0,0 +1,220 @@
+/*++
+
+Copyright (C) Microsoft Corporation, All Rights Reserved.
+
+Module Name:
+
+ Driver.cpp
+
+Abstract:
+
+ This module contains the implementation of the UMDF VirtualSerial Sample's
+ core driver callback object.
+
+Environment:
+
+ Windows User-Mode Driver Framework (WUDF)
+
+--*/
+
+#include "internal.h"
+#include "driver.tmh"
+
+HRESULT
+CMyDriver::CreateInstance(
+ _Out_ PCMyDriver *Driver
+ )
+/*++
+
+ Routine Description:
+
+ This static method is invoked in order to create and initialize a new
+ instance of the driver class. The caller should arrange for the object
+ to be released when it is no longer in use.
+
+ Arguments:
+
+ Driver - a location to store a referenced pointer to the new instance
+
+ Return Value:
+
+ S_OK if successful, or error otherwise.
+
+--*/
+{
+ PCMyDriver driver;
+ HRESULT hr;
+
+ //
+ // Allocate the callback object.
+ //
+
+ driver = new CMyDriver();
+
+ if (NULL == driver)
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ //
+ // Initialize the callback object.
+ //
+
+ hr = driver->Initialize();
+
+ if (SUCCEEDED(hr))
+ {
+ //
+ // Store a pointer to the new, initialized object in the output
+ // parameter.
+ //
+
+ *Driver = driver;
+ }
+ else
+ {
+
+ //
+ // Release the reference on the driver object to get it to delete
+ // itself.
+ //
+
+ driver->Release();
+ }
+
+ return hr;
+}
+
+HRESULT
+CMyDriver::Initialize(
+ VOID
+ )
+/*++
+
+ Routine Description:
+
+ This method is called to initialize a newly created driver callback object
+ before it is returned to the creator. Unlike the constructor, the
+ Initialize method contains operations which could potentially fail.
+
+ Arguments:
+
+ None
+
+ Return Value:
+
+ None
+
+--*/
+{
+ return S_OK;
+}
+
+HRESULT
+CMyDriver::QueryInterface(
+ _In_ REFIID InterfaceId,
+ _Out_ PVOID *Interface
+ )
+/*++
+
+ Routine Description:
+
+ This method returns a pointer to the requested interface on the callback
+ object..
+
+ Arguments:
+
+ InterfaceId - the IID of the interface to query/reference
+
+ Interface - a location to store the interface pointer.
+
+ Return Value:
+
+ S_OK if the interface is supported.
+ E_NOINTERFACE if it is not supported.
+
+--*/
+{
+ if (IsEqualIID(InterfaceId, __uuidof(IDriverEntry)))
+ {
+ *Interface = QueryIDriverEntry();
+ return S_OK;
+ }
+ else
+ {
+ return CUnknown::QueryInterface(InterfaceId, Interface);
+ }
+}
+
+HRESULT
+CMyDriver::OnDeviceAdd(
+ _In_ IWDFDriver *FxWdfDriver,
+ _In_ IWDFDeviceInitialize *FxDeviceInit
+ )
+/*++
+
+ Routine Description:
+
+ The FX invokes this method when it wants to install our driver on a device
+ stack. This method creates a device callback object, then calls the Fx
+ to create an Fx device object and associate the new callback object with
+ it.
+
+ Arguments:
+
+ FxWdfDriver - the Fx driver object.
+
+ FxDeviceInit - the initialization information for the device.
+
+ Return Value:
+
+ status
+
+--*/
+{
+ HRESULT hr;
+
+ PCMyDevice device = NULL;
+
+ //
+ // TODO: Do any per-device initialization (reading settings from the
+ // registry for example) that's necessary before creating your
+ // device callback object here. Otherwise you can leave such
+ // initialization to the initialization of the device event
+ // handler.
+ //
+
+ //
+ // Create a new instance of our device callback object
+ //
+
+ hr = CMyDevice::CreateInstance(FxWdfDriver, FxDeviceInit, &device);
+
+ //
+ // TODO: Change any per-device settings that the object exposes before
+ // calling Configure to let it complete its initialization.
+ //
+
+ //
+ // If that succeeded then call the device's construct method. This
+ // allows the device to create any queues or other structures that it
+ // needs now that the corresponding fx device object has been created.
+ //
+
+ if (SUCCEEDED(hr))
+ {
+ hr = device->Configure();
+ }
+
+ //
+ // Release the reference on the device callback object now that it's been
+ // associated with an fx device object.
+ //
+
+ if (NULL != device)
+ {
+ device->Release();
+ }
+
+ return hr;
+}
diff --git a/serial/VirtualSerial/driver.h b/serial/VirtualSerial/driver.h
new file mode 100644
index 00000000..5be609b1
--- /dev/null
+++ b/serial/VirtualSerial/driver.h
@@ -0,0 +1,149 @@
+/*++
+
+Copyright (C) Microsoft Corporation, All Rights Reserved
+
+Module Name:
+
+ Driver.h
+
+Abstract:
+
+ This module contains the type definitions for the UMDF VirtualSerial sample's
+ driver callback class.
+
+Environment:
+
+ Windows User-Mode Driver Framework (WUDF)
+
+--*/
+
+#pragma once
+
+//
+// This class handles driver events for the VirtualSerial sample. In particular
+// it supports the OnDeviceAdd event, which occurs when the driver is called
+// to setup per-device handlers for a new device stack.
+//
+
+class CMyDriver : public CUnknown, public IDriverEntry
+{
+//
+// Private data members.
+//
+private:
+
+//
+// Private methods.
+//
+private:
+
+ //
+ // Returns a refernced pointer to the IDriverEntry interface.
+ //
+
+ IDriverEntry *
+ QueryIDriverEntry(
+ VOID
+ )
+ {
+ AddRef();
+ return static_cast<IDriverEntry*>(this);
+ }
+
+ HRESULT
+ Initialize(
+ VOID
+ );
+
+//
+// Public methods
+//
+public:
+
+ //
+ // The factory method used to create an instance of this driver.
+ //
+
+ static
+ HRESULT
+ CreateInstance(
+ _Out_ PCMyDriver *Driver
+ );
+
+//
+// COM methods
+//
+public:
+
+ //
+ // IDriverEntry methods
+ //
+
+ virtual
+ HRESULT
+ STDMETHODCALLTYPE
+ OnInitialize(
+ _In_ IWDFDriver *FxWdfDriver
+ )
+ {
+ UNREFERENCED_PARAMETER( FxWdfDriver );
+
+ return S_OK;
+ }
+
+ virtual
+ HRESULT
+ STDMETHODCALLTYPE
+ OnDeviceAdd(
+ _In_ IWDFDriver *FxWdfDriver,
+ _In_ IWDFDeviceInitialize *FxDeviceInit
+ );
+
+ virtual
+ VOID
+ STDMETHODCALLTYPE
+ OnDeinitialize(
+ _In_ IWDFDriver *FxWdfDriver
+ )
+ {
+ UNREFERENCED_PARAMETER( FxWdfDriver );
+
+ return;
+ }
+
+ //
+ // IUnknown methods.
+ //
+ // We have to implement basic ones here that redirect to the
+ // base class becuase of the multiple inheritance.
+ //
+
+ virtual
+ ULONG
+ STDMETHODCALLTYPE
+ AddRef(
+ VOID
+ )
+ {
+ return __super::AddRef();
+ }
+
+ _At_(this, __drv_freesMem(object))
+ virtual
+ ULONG
+ STDMETHODCALLTYPE
+ Release(
+ VOID
+ )
+ {
+ return __super::Release();
+ }
+
+ virtual
+ HRESULT
+ STDMETHODCALLTYPE
+ QueryInterface(
+ _In_ REFIID InterfaceId,
+ _Out_ PVOID *Object
+ );
+};
diff --git a/serial/VirtualSerial/internal.h b/serial/VirtualSerial/internal.h
new file mode 100644
index 00000000..dd89de21
--- /dev/null
+++ b/serial/VirtualSerial/internal.h
@@ -0,0 +1,133 @@
+/*++
+
+Copyright (C) Microsoft Corporation, All Rights Reserved
+
+Module Name:
+
+ Internal.h
+
+Abstract:
+
+ This module contains the local type definitions for the UMDF VirtualSerial
+ driver sample.
+
+Environment:
+
+ Windows User-Mode Driver Framework (WUDF)
+
+--*/
+
+#pragma once
+
+#include <windows.h>
+#include <devioctl.h>
+#include <ntddser.h>
+#include <setupapi.h>
+#include <strsafe.h>
+#include <intsafe.h>
+
+//
+// Include the WUDF DDI
+//
+
+#include "wudfddi.h"
+
+//
+// Use specstrings for in/out annotation of function parameters.
+//
+
+#include "specstrings.h"
+
+//
+// Forward definitions of classes in the other header files.
+//
+
+typedef class CMyDriver *PCMyDriver;
+typedef class CMyDevice *PCMyDevice;
+typedef class CMyQueue *PCMyQueue;
+typedef class CRingBuffer *PCMyRingBuffer;
+
+//
+// Define useful macros
+//
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
+#endif
+
+#define SAFE_RELEASE(p) {if ((p)) { (p)->Release(); (p) = NULL; }}
+
+//
+// Define the tracing flags.
+//
+
+#define WPP_CONTROL_GUIDS \
+ WPP_DEFINE_CONTROL_GUID( \
+ MyDriverTraceControl, (C31877A2,C8C8,4c58,B5B8,7D6311D5E343), \
+ \
+ 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\\VirtualSerial"
+#define MYDRIVER_CLASS_ID { 0xc8ecc087, 0x6b79, 0x4de5, { 0x8f, 0xb4, 0xc0, 0x33, 0x58, 0xa2, 0x96, 0x17 } }
+
+__forceinline
+#ifdef _PREFAST_
+__declspec(noreturn)
+#endif
+VOID
+WdfTestNoReturn(
+ VOID
+ )
+{
+ // do nothing.
+}
+
+#define WUDF_TEST_DRIVER_ASSERT(p) \
+{ \
+ if ( !(p) ) \
+ { \
+ DebugBreak(); \
+ WdfTestNoReturn(); \
+ } \
+}
+
+#define MAXULONG 0xffffffff
+
+//
+// Device states
+//
+#define COMMAND_MATCH_STATE_IDLE 0
+#define COMMAND_MATCH_STATE_GOT_A 1
+#define COMMAND_MATCH_STATE_GOT_T 2
+
+//
+// Include the type specific headers.
+//
+
+#include "comsup.h"
+#include "driver.h"
+#include "device.h"
+#include "ringbuffer.h"
+#include "queue.h"
+#include "serial.h"
diff --git a/serial/VirtualSerial/queue.cpp b/serial/VirtualSerial/queue.cpp
new file mode 100644
index 00000000..5a7b5bad
--- /dev/null
+++ b/serial/VirtualSerial/queue.cpp
@@ -0,0 +1,1188 @@
+/*++
+
+Copyright (c) Microsoft Corporation, All Rights Reserved
+
+Module Name:
+
+ queue.cpp
+
+Abstract:
+
+ This file implements the I/O queue interface and performs
+ the read/write/ioctl operations.
+
+Environment:
+
+ Windows User-Mode Driver Framework (WUDF)
+
+--*/
+
+
+#include "internal.h"
+
+//
+// IUnknown implementation
+//
+
+//
+// Queue destructor.
+// Free up the buffer, wait for thread to terminate and
+// delete critical section.
+//
+
+
+CMyQueue::~CMyQueue(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+
+ IUnknown implementation of Release
+
+Arguments:
+
+
+Return Value:
+
+--*/
+{
+ WUDF_TEST_DRIVER_ASSERT(m_Device);
+
+ m_Device->Release();
+}
+
+
+//
+// Initialize
+HRESULT
+CMyQueue::CreateInstance(
+ _In_ CMyDevice *pDevice,
+ _In_ IWDFDevice *FxDevice,
+ _Out_ PCMyQueue *Queue
+ )
+/*++
+
+Routine Description:
+
+
+ CreateInstance creates an instance of the queue object.
+
+Arguments:
+
+ ppUkwn - OUT parameter is an IUnknown interface to the queue object
+
+Return Value:
+
+ HRESULT indicating success or failure
+
+--*/
+{
+ CMyQueue *pMyQueue = new CMyQueue(pDevice);
+ HRESULT hr;
+
+ if (pMyQueue == NULL) {
+ return E_OUTOFMEMORY;
+ }
+
+ hr = pMyQueue->Initialize(FxDevice);
+
+ if (SUCCEEDED(hr))
+ {
+ *Queue = pMyQueue;
+ }
+ else
+ {
+ pMyQueue->Release();
+ }
+ return hr;
+}
+
+HRESULT
+CMyQueue::Initialize(
+ _In_ IWDFDevice *FxDevice
+ )
+{
+ IWDFIoQueue *fxQueue;
+ IUnknown *unknown = NULL;
+ HRESULT hr;
+
+ //
+ // Initialize ring buffer
+ //
+
+ hr = m_RingBuffer.Initialize(DATA_BUFFER_SIZE);
+
+ if (FAILED(hr))
+ {
+ goto Exit;
+ }
+
+ unknown = QueryIUnknown();
+
+ //
+ // Create the default queue
+ //
+
+ {
+ hr = FxDevice->CreateIoQueue(unknown,
+ TRUE,
+ WdfIoQueueDispatchParallel,
+ TRUE,
+ FALSE,
+ &fxQueue);
+ }
+
+ if (FAILED(hr))
+ {
+ goto Exit;
+ }
+
+ m_FxQueue = fxQueue;
+
+ fxQueue->Release();
+
+ //
+ // Create a manual queue to hold pending read requests. By keeping
+ // them in the queue, framework takes care of cancelling them if the app
+ // exits
+ //
+
+ {
+ hr = FxDevice->CreateIoQueue(NULL,
+ FALSE,
+ WdfIoQueueDispatchManual,
+ TRUE,
+ FALSE,
+ &fxQueue);
+ }
+
+ if (FAILED(hr))
+ {
+ goto Exit;
+ }
+
+ m_FxReadQueue = fxQueue;
+
+ fxQueue->Release();
+
+ //
+ // Create a manual queue to hold pending ioctl wait-on-mask requests.
+ //
+
+ {
+ hr = FxDevice->CreateIoQueue(NULL,
+ FALSE,
+ WdfIoQueueDispatchManual,
+ TRUE,
+ FALSE,
+ &fxQueue);
+ }
+
+ if (FAILED(hr))
+ {
+ goto Exit;
+ }
+
+ m_FxWaitMaskQueue = fxQueue;
+
+ fxQueue->Release();
+
+Exit:
+ SAFE_RELEASE(unknown);
+ return hr;
+}
+
+HRESULT
+STDMETHODCALLTYPE
+CMyQueue::QueryInterface(
+ _In_ REFIID InterfaceId,
+ _Out_ PVOID *Object
+ )
+/*++
+
+Routine Description:
+
+
+ Query Interface
+
+Arguments:
+
+ Follows COM specifications
+
+Return Value:
+
+ HRESULT indicating success or failure
+
+--*/
+{
+ HRESULT hr;
+
+
+ if (IsEqualIID(InterfaceId, __uuidof(IQueueCallbackWrite))) {
+ *Object = QueryIQueueCallbackWrite();
+ hr = S_OK;
+ } else if (IsEqualIID(InterfaceId, __uuidof(IQueueCallbackRead))) {
+ *Object = QueryIQueueCallbackRead();
+ hr = S_OK;
+ } else if (IsEqualIID(InterfaceId, __uuidof(IQueueCallbackDeviceIoControl))) {
+ *Object = QueryIQueueCallbackDeviceIoControl();
+ hr = S_OK;
+ } else {
+ hr = CUnknown::QueryInterface(InterfaceId, Object);
+ }
+
+ return hr;
+}
+
+VOID
+STDMETHODCALLTYPE
+CMyQueue::OnDeviceIoControl(
+ _In_ IWDFIoQueue *pWdfQueue,
+ _In_ IWDFIoRequest *pWdfRequest,
+ _In_ ULONG ControlCode,
+ _In_ SIZE_T InputBufferSizeInBytes,
+ _In_ SIZE_T OutputBufferSizeInBytes
+ )
+/*++
+
+Routine Description:
+
+
+ DeviceIoControl dispatch routine
+
+Arguments:
+
+ pWdfQueue - Framework Queue instance
+ pWdfRequest - Framework Request instance
+ ControlCode - IO Control Code
+ InputBufferSizeInBytes - Length of input buffer
+ OutputBufferSizeInBytes - Length of output buffer
+
+ Always succeeds DeviceIoIoctl
+Return Value:
+
+ VOID
+
+--*/
+{
+ UNREFERENCED_PARAMETER(OutputBufferSizeInBytes);
+ UNREFERENCED_PARAMETER(InputBufferSizeInBytes);
+ UNREFERENCED_PARAMETER(pWdfQueue);
+
+ HRESULT hr = S_OK;
+ SIZE_T reqCompletionInfo = 0;
+ IWDFMemory *inputMemory = NULL;
+ IWDFMemory *outputMemory = NULL;
+ UINT i;
+
+ WUDF_TEST_DRIVER_ASSERT(pWdfRequest);
+ WUDF_TEST_DRIVER_ASSERT(m_Device);
+
+ switch (ControlCode)
+ {
+ case IOCTL_SERIAL_SET_BAUD_RATE:
+ {
+ //
+ // This is a driver for a virtual serial port. Since there is no
+ // actual hardware, we just store the baud rate and don't do
+ // anything with it.
+ //
+ SERIAL_BAUD_RATE baudRateBuffer = {0};
+
+ pWdfRequest->GetInputMemory(&inputMemory);
+ if (NULL == inputMemory)
+ {
+ hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ hr = inputMemory->CopyToBuffer(0,
+ (void*) &baudRateBuffer,
+ sizeof(SERIAL_BAUD_RATE));
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ m_Device->SetBaudRate(baudRateBuffer.BaudRate);
+ }
+
+ break;
+ }
+ case IOCTL_SERIAL_GET_BAUD_RATE:
+ {
+ SERIAL_BAUD_RATE baudRateBuffer = {0};
+
+ baudRateBuffer.BaudRate = m_Device->GetBaudRate();
+
+ pWdfRequest->GetOutputMemory(&outputMemory);
+ if (NULL == outputMemory)
+ {
+ hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ hr = outputMemory->CopyFromBuffer(0,
+ (void*) &baudRateBuffer,
+ sizeof(SERIAL_BAUD_RATE));
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ reqCompletionInfo = sizeof(SERIAL_BAUD_RATE);
+ }
+
+ break;
+ }
+ case IOCTL_SERIAL_SET_MODEM_CONTROL:
+ {
+ //
+ // This is a driver for a virtual serial port. Since there is no
+ // actual hardware, we just store the modem control register
+ // configuration and don't do anything with it.
+ //
+ ULONG *pModemControlRegister = NULL;
+
+ pWdfRequest->GetInputMemory(&inputMemory);
+ if (NULL == inputMemory)
+ {
+ hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ pModemControlRegister = m_Device->GetModemControlRegisterPtr();
+ WUDF_TEST_DRIVER_ASSERT(pModemControlRegister);
+
+ hr = inputMemory->CopyToBuffer(0,
+ (void*) pModemControlRegister,
+ sizeof(ULONG));
+ }
+
+ break;
+ }
+ case IOCTL_SERIAL_GET_MODEM_CONTROL:
+ {
+ ULONG *pModemControlRegister = NULL;
+
+ pWdfRequest->GetOutputMemory(&outputMemory);
+ if (NULL == outputMemory)
+ {
+ hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ pModemControlRegister = m_Device->GetModemControlRegisterPtr();
+ WUDF_TEST_DRIVER_ASSERT(pModemControlRegister);
+
+ hr = outputMemory->CopyFromBuffer(0,
+ (void*) pModemControlRegister,
+ sizeof(ULONG));
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ reqCompletionInfo = sizeof(ULONG);
+ }
+
+ break;
+ }
+ case IOCTL_SERIAL_SET_FIFO_CONTROL:
+ {
+ //
+ // This is a driver for a virtual serial port. Since there is no
+ // actual hardware, we just store the FIFO control register
+ // configuration and don't do anything with it.
+ //
+ ULONG *pFifoControlRegister = NULL;
+
+ pWdfRequest->GetInputMemory(&inputMemory);
+ if (NULL == inputMemory)
+ {
+ hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ pFifoControlRegister = m_Device->GetFifoControlRegisterPtr();
+
+ hr = inputMemory->CopyToBuffer(0,
+ (void*) pFifoControlRegister,
+ sizeof(ULONG));
+ }
+
+ break;
+ }
+ case IOCTL_SERIAL_GET_LINE_CONTROL:
+ {
+ ULONG *pLineControlRegister = NULL;
+ SERIAL_LINE_CONTROL lineControl = {0};
+ ULONG lineControlSnapshot;
+
+ pLineControlRegister = m_Device->GetLineControlRegisterPtr();
+ WUDF_TEST_DRIVER_ASSERT(pLineControlRegister);
+
+ //
+ // Take a snapshot of the line control register variable
+ //
+ lineControlSnapshot = *pLineControlRegister;
+
+ //
+ // Decode the word length
+ //
+ if ((lineControlSnapshot & SERIAL_DATA_MASK) == SERIAL_5_DATA)
+ {
+ lineControl.WordLength = 5;
+ }
+ else if ((lineControlSnapshot & SERIAL_DATA_MASK) == SERIAL_6_DATA)
+ {
+ lineControl.WordLength = 6;
+ }
+ else if ((lineControlSnapshot & SERIAL_DATA_MASK) == SERIAL_7_DATA)
+ {
+ lineControl.WordLength = 7;
+ }
+ else if ((lineControlSnapshot & SERIAL_DATA_MASK) == SERIAL_8_DATA)
+ {
+ lineControl.WordLength = 8;
+ }
+
+ //
+ // Decode the parity
+ //
+ if ((lineControlSnapshot & SERIAL_PARITY_MASK) == SERIAL_NONE_PARITY)
+ {
+ lineControl.Parity = NO_PARITY;
+ }
+ else if ((lineControlSnapshot & SERIAL_PARITY_MASK) == SERIAL_ODD_PARITY)
+ {
+ lineControl.Parity = ODD_PARITY;
+ }
+ else if ((lineControlSnapshot & SERIAL_PARITY_MASK) == SERIAL_EVEN_PARITY)
+ {
+ lineControl.Parity = EVEN_PARITY;
+ }
+ else if ((lineControlSnapshot & SERIAL_PARITY_MASK) == SERIAL_MARK_PARITY)
+ {
+ lineControl.Parity = MARK_PARITY;
+ }
+ else if ((lineControlSnapshot & SERIAL_PARITY_MASK) == SERIAL_SPACE_PARITY)
+ {
+ lineControl.Parity = SPACE_PARITY;
+ }
+
+ //
+ // Decode the length of the stop bit
+ //
+ if (lineControlSnapshot & SERIAL_2_STOP)
+ {
+ if (lineControl.WordLength == 5)
+ {
+ lineControl.StopBits = STOP_BITS_1_5;
+ }
+ else
+ {
+ lineControl.StopBits = STOP_BITS_2;
+ }
+ }
+ else
+ {
+ lineControl.StopBits = STOP_BIT_1;
+ }
+
+ //
+ // Copy the information that was decoded to the caller's buffer
+ //
+ pWdfRequest->GetOutputMemory(&outputMemory);
+ if (NULL == outputMemory)
+ {
+ hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ hr = outputMemory->CopyFromBuffer(0,
+ (void*) &lineControl,
+ sizeof(SERIAL_LINE_CONTROL));
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ reqCompletionInfo = sizeof(SERIAL_LINE_CONTROL);
+ }
+
+ break;
+ }
+ case IOCTL_SERIAL_SET_LINE_CONTROL:
+ {
+ ULONG *pLineControlRegister = NULL;
+ SERIAL_LINE_CONTROL lineControl = {0};
+ UCHAR lineControlData = 0;
+ UCHAR lineControlStop = 0;
+ UCHAR lineControlParity = 0;
+ ULONG lineControlSnapshot;
+ ULONG lineControlNew;
+ ULONG lineControlPrevious;
+
+ pLineControlRegister = m_Device->GetLineControlRegisterPtr();
+ WUDF_TEST_DRIVER_ASSERT(pLineControlRegister);
+
+ //
+ // This is a driver for a virtual serial port. Since there is no
+ // actual hardware, we just store the line control register
+ // configuration and don't do anything with it.
+ //
+ pWdfRequest->GetInputMemory(&inputMemory);
+ if (NULL == inputMemory)
+ {
+ hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ hr = inputMemory->CopyToBuffer(0,
+ (void*) &lineControl,
+ sizeof(SERIAL_LINE_CONTROL));
+ }
+
+ //
+ // Bits 0 and 1 of the line control register
+ //
+ if (SUCCEEDED(hr))
+ {
+ switch (lineControl.WordLength)
+ {
+ case 5:
+ lineControlData = SERIAL_5_DATA;
+ m_Device->SetValidDataMask(0x1f);
+ break;
+
+ case 6:
+ lineControlData = SERIAL_6_DATA;
+ m_Device->SetValidDataMask(0x3f);
+ break;
+
+ case 7:
+ lineControlData = SERIAL_7_DATA;
+ m_Device->SetValidDataMask(0x7f);
+ break;
+
+ case 8:
+ lineControlData = SERIAL_8_DATA;
+ m_Device->SetValidDataMask(0xff);
+ break;
+
+ default:
+ hr = E_INVALIDARG;
+ }
+ }
+
+ //
+ // Bit 2 of the line control register
+ //
+ if (SUCCEEDED(hr))
+ {
+ switch (lineControl.StopBits)
+ {
+ case STOP_BIT_1:
+ lineControlStop = SERIAL_1_STOP;
+ break;
+
+ case STOP_BITS_1_5:
+ if (lineControlData != SERIAL_5_DATA)
+ {
+ hr = E_INVALIDARG;
+ break;
+ }
+ lineControlStop = SERIAL_1_5_STOP;
+ break;
+
+ case STOP_BITS_2:
+ if (lineControlData == SERIAL_5_DATA)
+ {
+ hr = E_INVALIDARG;
+ break;
+ }
+ lineControlStop = SERIAL_2_STOP;
+ break;
+
+ default:
+ hr = E_INVALIDARG;
+ }
+ }
+
+ //
+ // Bits 3, 4 and 5 of the line control register
+ //
+ if (SUCCEEDED(hr))
+ {
+ switch (lineControl.Parity)
+ {
+ case NO_PARITY:
+ lineControlParity = SERIAL_NONE_PARITY;
+ break;
+
+ case EVEN_PARITY:
+ lineControlParity = SERIAL_EVEN_PARITY;
+ break;
+
+ case ODD_PARITY:
+ lineControlParity = SERIAL_ODD_PARITY;
+ break;
+
+ case SPACE_PARITY:
+ lineControlParity = SERIAL_SPACE_PARITY;
+ break;
+
+ case MARK_PARITY:
+ lineControlParity = SERIAL_MARK_PARITY;
+ break;
+
+ default:
+ hr = E_INVALIDARG;
+ }
+ }
+
+ //
+ // Update our line control register variable atomically
+ //
+ i=0;
+ do
+ {
+ i++;
+ if ((i & 0xf) == 0)
+ {
+ //
+ // We've been spinning in a loop for a while trying to
+ // update the line control register variable atomically.
+ // Yield the CPU for other threads for a while.
+ //
+ SwitchToThread();
+ }
+
+ lineControlSnapshot = *pLineControlRegister;
+
+ lineControlNew = (lineControlSnapshot & SERIAL_LCR_BREAK) |
+ (lineControlData |
+ lineControlParity |
+ lineControlStop);
+
+ lineControlPrevious = InterlockedCompareExchange((LONG *) pLineControlRegister,
+ lineControlNew,
+ lineControlSnapshot);
+
+ } while (lineControlPrevious != lineControlSnapshot);
+
+ break;
+ }
+ case IOCTL_SERIAL_GET_TIMEOUTS:
+ {
+ SERIAL_TIMEOUTS timeoutValues = {0};
+
+ pWdfRequest->GetOutputMemory(&outputMemory);
+ if (NULL == outputMemory)
+ {
+ hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ m_Device->GetTimeouts(&timeoutValues);
+
+ hr = outputMemory->CopyFromBuffer(0,
+ (void*) &timeoutValues,
+ sizeof(timeoutValues));
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ reqCompletionInfo = sizeof(SERIAL_TIMEOUTS);
+ }
+
+ break;
+ }
+ case IOCTL_SERIAL_SET_TIMEOUTS:
+ {
+ SERIAL_TIMEOUTS timeoutValues = {0};
+
+ pWdfRequest->GetInputMemory(&inputMemory);
+ if (NULL == inputMemory)
+ {
+ hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ hr = inputMemory->CopyToBuffer(0,
+ (void*) &timeoutValues,
+ sizeof(timeoutValues));
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ if ((timeoutValues.ReadIntervalTimeout == MAXULONG) &&
+ (timeoutValues.ReadTotalTimeoutMultiplier == MAXULONG) &&
+ (timeoutValues.ReadTotalTimeoutConstant == MAXULONG))
+ {
+ hr = E_INVALIDARG;
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ m_Device->SetTimeouts(timeoutValues);
+ }
+
+ break;
+ }
+ case IOCTL_SERIAL_WAIT_ON_MASK:
+ {
+ //
+ // NOTE: the contract is that this ioctl should be marked pending
+ // and not to be completed until some wait event happens. Therefore
+ // it is incorrect for the driver to complete the ioctl right away,
+ // no matter whether success or failure is returned. In either case
+ // most likely the app will send down another iocl of wait-on-mask
+ // in a tight loop, and that gets completed again, and again.
+ // The end result will be high CPU utilization in task manager.
+ //
+ // The correct way would be to mark the ioctl as pending, or as in
+ // WDF world, keep it in a manual queue. Since this is a driver for
+ // a virtual serial port and there is no actual hardware, there will
+ // be no wait event happening. The ioctl stays in the queue until
+ // the calling app decides to no longer wait for it by means of
+ // sending down a set-wait-mask request.
+ //
+
+ //
+ // At most one pending wait-on-mask request is expected
+ //
+ IWDFIoRequest *pSavedRequest;
+ hr = m_FxWaitMaskQueue->RetrieveNextRequest(&pSavedRequest);
+ if (SUCCEEDED(hr))
+ {
+ pSavedRequest->Complete(E_FAIL);
+
+ //
+ // RetrieveNextRequest from a manual queue increments the reference
+ // counter by 1. We need to decrement it, otherwise the request will
+ // not be released and there will be an object leak.
+ //
+ SAFE_RELEASE(pSavedRequest);
+ }
+
+ //
+ // Keep the request in a manual queue and the framework will take
+ // care of cancelling them when the app exits.
+ //
+ pWdfRequest->ForwardToIoQueue(m_FxWaitMaskQueue);
+
+ //
+ // Instead of "break" out of the switch statement and complete the
+ // request at the end of this function, use "return" directly.
+ //
+ return;
+ }
+ case IOCTL_SERIAL_SET_WAIT_MASK:
+ {
+ //
+ // NOTE: the contract says a set-wait-mask will cause any pending
+ // wait-on-mask to complete with STATUS_SUCCESS and the output wait
+ // event mask is set to zero. This is also the way for app to break
+ // out of the loop of sending down wait-on-mask
+ //
+
+ IWDFIoRequest *pSavedRequest;
+ hr = m_FxWaitMaskQueue->RetrieveNextRequest(&pSavedRequest);
+ if (SUCCEEDED(hr))
+ {
+ pSavedRequest->GetOutputMemory(&outputMemory);
+ if (NULL == outputMemory)
+ {
+ hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ ULONG eventMask = 0;
+
+ hr = outputMemory->CopyFromBuffer(0,
+ (void*) &eventMask,
+ sizeof(eventMask));
+
+ pSavedRequest->CompleteWithInformation(hr, sizeof(eventMask));
+ }
+ else
+ {
+ pSavedRequest->Complete(hr);
+ }
+
+ SAFE_RELEASE(pSavedRequest);
+
+ //
+ // outputMemory will be released at the end of the function
+ //
+ }
+
+ //
+ // NOTE: The application expects STATUS_SUCCESS for these IOCTLs.
+ //
+ hr = S_OK;
+
+ break;
+ }
+ case IOCTL_SERIAL_SET_QUEUE_SIZE:
+ case IOCTL_SERIAL_SET_DTR:
+ case IOCTL_SERIAL_SET_RTS:
+ case IOCTL_SERIAL_CLR_RTS:
+ case IOCTL_SERIAL_SET_XON:
+ case IOCTL_SERIAL_SET_XOFF:
+ case IOCTL_SERIAL_SET_CHARS:
+ case IOCTL_SERIAL_GET_CHARS:
+ case IOCTL_SERIAL_GET_HANDFLOW:
+ case IOCTL_SERIAL_SET_HANDFLOW:
+ case IOCTL_SERIAL_RESET_DEVICE:
+ {
+ //
+ // NOTE: The application expects STATUS_SUCCESS for these IOCTLs.
+ // so don't merge this with default.
+ //
+ break;
+ }
+
+ default:
+ {
+ hr = E_INVALIDARG;
+ }
+ }
+
+ //
+ // clean up
+ //
+ if (inputMemory)
+ {
+ inputMemory->Release();
+ }
+
+ if (outputMemory)
+ {
+ outputMemory->Release();
+ }
+
+ //
+ // complete the request
+ //
+ pWdfRequest->CompleteWithInformation(hr, reqCompletionInfo);
+
+ return;
+}
+
+VOID
+STDMETHODCALLTYPE
+CMyQueue::OnWrite(
+ _In_ IWDFIoQueue *pWdfQueue,
+ _In_ IWDFIoRequest *pWdfRequest,
+ _In_ SIZE_T BytesToWrite
+ )
+/*++
+
+Routine Description:
+
+
+ Write dispatch routine
+ IQueueCallbackWrite
+
+Arguments:
+
+ pWdfQueue - Framework Queue instance
+ pWdfRequest - Framework Request instance
+ BytesToWrite - Length of bytes in the write buffer
+
+ Allocate and copy data to local buffer
+Return Value:
+
+ VOID
+
+--*/
+{
+ IWDFMemory* pRequestMemory = NULL;
+ IWDFIoRequest* pSavedRequest = NULL;
+ SIZE_T availableData = 0;
+ SIZE_T savedRequestBufferSize = 0;
+ HRESULT hr = S_OK;
+
+ UNREFERENCED_PARAMETER(pWdfQueue);
+
+ //
+ // Get memory object
+ //
+
+ pWdfRequest->GetInputMemory(&pRequestMemory);
+
+ //
+ // Process input
+ //
+
+ ProcessWriteBytes((PUCHAR)pRequestMemory->GetDataBuffer(NULL), BytesToWrite);
+
+ //
+ // Release memory object and complete request
+ //
+ SAFE_RELEASE(pRequestMemory);
+ pWdfRequest->CompleteWithInformation(hr, BytesToWrite);
+
+ //
+ // Get the amount of data available in the ring buffer
+ //
+ m_RingBuffer.GetAvailableData(&availableData);
+
+ if (availableData > 0)
+ {
+ //
+ // Continue with the next request, if there is one pending
+ //
+ hr = m_FxReadQueue->RetrieveNextRequest(&pSavedRequest);
+ if ((pSavedRequest == NULL) || (FAILED(hr)))
+ {
+ goto Exit;
+ }
+
+ pSavedRequest->GetReadParameters(&savedRequestBufferSize, NULL, NULL);
+
+ OnRead(m_FxQueue,
+ pSavedRequest,
+ savedRequestBufferSize);
+
+ //
+ // RetrieveNextRequest from a manual queue increments the reference
+ // counter by 1. We need to decrement it, otherwise the request will
+ // not be released and there will be an object leak.
+ //
+ SAFE_RELEASE(pSavedRequest);
+ }
+
+Exit:
+ return;
+}
+
+VOID
+STDMETHODCALLTYPE
+CMyQueue::OnRead(
+ _In_ IWDFIoQueue *pWdfQueue,
+ _In_ IWDFIoRequest *pWdfRequest,
+ _In_ SIZE_T SizeInBytes
+ )
+/*++
+
+Routine Description:
+
+
+ Read dispatch routine
+ IQueueCallbackRead
+
+Arguments:
+
+ pWdfQueue - Framework Queue instance
+ pWdfRequest - Framework Request instance
+ SizeInBytes - Length of bytes in the read buffer
+
+ Copy available data into the read buffer
+Return Value:
+
+ VOID
+
+--*/
+{
+ IWDFMemory* pRequestMemory = NULL;
+ SIZE_T BytesCopied = 0;
+ HRESULT hr;
+
+ UNREFERENCED_PARAMETER(pWdfQueue);
+
+ //
+ // Get memory object
+ //
+ pWdfRequest->GetOutputMemory(&pRequestMemory);
+
+ hr = m_RingBuffer.Read((PBYTE)pRequestMemory->GetDataBuffer(NULL),
+ SizeInBytes,
+ &BytesCopied);
+
+ //
+ // Release memory object.
+ //
+ SAFE_RELEASE(pRequestMemory);
+
+ if (FAILED(hr))
+ {
+ //
+ // Error reading buffer
+ //
+ pWdfRequest->Complete(hr);
+ goto Exit;
+ }
+
+ if (BytesCopied > 0)
+ {
+ //
+ // Data was read from buffer succesfully
+ //
+ pWdfRequest->CompleteWithInformation(hr, BytesCopied);
+ }
+ else
+ {
+ //
+ // No data to read. Queue the request for later processing.
+ //
+ pWdfRequest->ForwardToIoQueue(m_FxReadQueue);
+ }
+
+Exit:
+ return;
+}
+
+VOID
+CMyQueue::ProcessWriteBytes(
+ _In_reads_bytes_(Length) PUCHAR Characters,
+ _In_ SIZE_T Length
+ )
+/*++
+Routine Description:
+
+ This function is called when the framework receives IRP_MJ_WRITE
+ requests from the system. The write event handler(FmEvtIoWrite) calls ProcessWriteBytes.
+ It parses the Characters passed in and looks for the for sequences "AT" -ok ,
+ "ATA" --CONNECT, ATD<number> -- CONNECT and sets the state of the device appropriately.
+ These bytes are placed in the read Buffer to be processed later since this device
+ works in a loopback fashion.
+
+
+Arguments:
+
+ Characters - Pointer to the write IRP's system buffer.
+
+ Length - Length of the IO operation
+ The default property of the queue is to not dispatch
+ zero lenght read & write requests to the driver and
+ complete is with status success. So we will never get
+ a zero length request.
+
+Return Value:
+
+ VOID
+
+--*/
+
+{
+
+ UCHAR currentCharacter;
+ UCHAR connectString[] = "\r\nCONNECT\r\n";
+ UCHAR connectStringCch = ARRAY_SIZE(connectString) - 1;
+ UCHAR okString[] = "\r\nOK\r\n";
+ UCHAR okStringCch = ARRAY_SIZE(okString) - 1;
+
+ while (Length != 0) {
+
+ currentCharacter = *(Characters++);
+ Length--;
+
+ if(currentCharacter == '\0')
+ {
+ continue;
+ }
+
+ m_RingBuffer.Write(&currentCharacter, sizeof(currentCharacter));
+
+ switch (m_CommandMatchState) {
+ case COMMAND_MATCH_STATE_IDLE:
+ if ((currentCharacter == 'a') || (currentCharacter == 'A'))
+ {
+ //
+ // got an A
+ //
+ m_CommandMatchState=COMMAND_MATCH_STATE_GOT_A;
+ m_ConnectCommand=FALSE;
+ m_IgnoreNextChar=FALSE;
+ }
+
+ break;
+
+ case COMMAND_MATCH_STATE_GOT_A:
+ if ((currentCharacter == 't') || (currentCharacter == 'T'))
+ {
+ //
+ // got a T
+ //
+ m_CommandMatchState=COMMAND_MATCH_STATE_GOT_T;
+ }
+ else
+ {
+ m_CommandMatchState=COMMAND_MATCH_STATE_IDLE;
+ }
+
+ break;
+
+ case COMMAND_MATCH_STATE_GOT_T:
+
+ if (!m_IgnoreNextChar)
+ {
+ //
+ // the last char was not a special char
+ // check for CONNECT command
+ //
+ if ((currentCharacter == 'A') || (currentCharacter == 'a'))
+ {
+
+ m_ConnectCommand=TRUE;
+ }
+
+ if ((currentCharacter == 'D') || (currentCharacter == 'd'))
+ {
+
+ m_ConnectCommand=TRUE;
+ }
+ }
+
+ m_IgnoreNextChar=TRUE;
+
+ if (currentCharacter == '\r')
+ {
+ //
+ // got a CR, send a response to the command
+ //
+ m_CommandMatchState = COMMAND_MATCH_STATE_IDLE;
+
+ if (m_ConnectCommand)
+ {
+ //
+ // place <cr><lf>CONNECT<cr><lf> in the buffer
+ //
+ m_RingBuffer.Write(connectString, connectStringCch);
+
+ //
+ // connected now raise CD
+ //
+ m_CurrentlyConnected = TRUE;
+ m_ConnectionStateChanged = TRUE;
+
+ }
+ else
+ {
+ //
+ // place <cr><lf>OK<cr><lf> in the buffer
+ //
+ m_RingBuffer.Write(okString, okStringCch);
+ }
+ }
+
+ break;
+ default:
+ break;
+ }
+ }
+ return;
+}
diff --git a/serial/VirtualSerial/queue.h b/serial/VirtualSerial/queue.h
new file mode 100644
index 00000000..445ee48e
--- /dev/null
+++ b/serial/VirtualSerial/queue.h
@@ -0,0 +1,206 @@
+/*++
+
+Copyright (c) Microsoft Corporation, All Rights Reserved
+
+Module Name:
+
+ queue.h
+
+Abstract:
+
+ This file defines the queue callback interface.
+
+Environment:
+
+ Windows User-Mode Driver Framework (WUDF)
+
+--*/
+
+#pragma once
+
+#include "internal.h"
+
+// Set ring buffer size
+#define DATA_BUFFER_SIZE 1024
+
+//
+// Macro definition for defining IOCTL and FSCTL function control codes. Note
+// that function codes 0-2047 are reserved for Microsoft Corporation, and
+// 2048-4095 are reserved for customers.
+//
+
+#define CTL_CODE( DeviceType, Function, Method, Access ) ( \
+ ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method) \
+)
+
+
+//
+// Queue Callback Object.
+//
+
+class CMyQueue :
+ public IQueueCallbackDeviceIoControl,
+ public IQueueCallbackRead,
+ public IQueueCallbackWrite,
+ public CUnknown
+{
+ UCHAR m_CommandMatchState; // Device state
+ BOOL m_ConnectCommand;
+ BOOL m_IgnoreNextChar;
+ BOOL m_ConnectionStateChanged;
+ BOOL m_CurrentlyConnected;
+
+ IWDFIoQueue *m_FxQueue; // Default parallel queue
+ IWDFIoQueue *m_FxReadQueue; // Manual queue for pending reads
+ IWDFIoQueue *m_FxWaitMaskQueue; // Manual queue for pending ioctl wait-on-mask
+
+ CRingBuffer m_RingBuffer; // Ring buffer for pending data
+
+ PCMyDevice m_Device;
+
+ CMyQueue(
+ CMyDevice *pDevice
+ ) :
+ m_CommandMatchState(COMMAND_MATCH_STATE_IDLE),
+ m_ConnectCommand(FALSE),
+ m_IgnoreNextChar(FALSE),
+ m_ConnectionStateChanged(FALSE),
+ m_CurrentlyConnected(FALSE),
+ m_FxQueue(NULL),
+ m_FxReadQueue(NULL)
+ {
+ WUDF_TEST_DRIVER_ASSERT(pDevice);
+
+ pDevice->AddRef();
+ m_Device = pDevice;
+ }
+
+ virtual ~CMyQueue();
+
+ HRESULT
+ Initialize(
+ _In_ IWDFDevice *FxDevice
+ );
+
+ VOID
+ CMyQueue::ProcessWriteBytes(
+ _In_reads_bytes_(Length) PUCHAR Characters,
+ _In_ SIZE_T Length
+ );
+
+public:
+
+ static
+ HRESULT
+ CreateInstance(
+ _In_ CMyDevice *pDevice,
+ _In_ IWDFDevice *FxDevice,
+ _Out_ PCMyQueue *Queue
+ );
+
+ HRESULT
+ Configure(
+ VOID
+ )
+ {
+ return S_OK;
+ }
+
+
+ IQueueCallbackDeviceIoControl *
+ QueryIQueueCallbackDeviceIoControl(
+ VOID
+ )
+ {
+ AddRef();
+ return static_cast<IQueueCallbackDeviceIoControl *>(this);
+ }
+
+ IQueueCallbackRead *
+ QueryIQueueCallbackRead(
+ VOID
+ )
+ {
+ AddRef();
+ return static_cast<IQueueCallbackRead *>(this);
+ }
+
+ IQueueCallbackWrite *
+ QueryIQueueCallbackWrite(
+ VOID
+ )
+ {
+ AddRef();
+ return static_cast<IQueueCallbackWrite *>(this);
+ }
+
+ //
+ // IUnknown
+ //
+
+ virtual
+ ULONG
+ STDMETHODCALLTYPE
+ AddRef(
+ VOID
+ ) {
+ return CUnknown::AddRef();
+ }
+
+ _At_(this, __drv_freesMem(object))
+ virtual
+ ULONG
+ STDMETHODCALLTYPE
+ Release(
+ VOID
+ ) {
+ return CUnknown::Release();
+ }
+
+ virtual
+ HRESULT
+ STDMETHODCALLTYPE
+ QueryInterface(
+ _In_ REFIID InterfaceId,
+ _Out_ PVOID *Object
+ );
+
+ //
+ // Wdf Callbacks
+ //
+
+ // IQueueCallbackDeviceIoControl
+ //
+ virtual
+ VOID
+ STDMETHODCALLTYPE
+ OnDeviceIoControl(
+ _In_ IWDFIoQueue *pWdfQueue,
+ _In_ IWDFIoRequest *pWdfRequest,
+ _In_ ULONG ControlCode,
+ _In_ SIZE_T InputBufferSizeInBytes,
+ _In_ SIZE_T OutputBufferSizeInBytes
+ );
+
+ // IQueueCallbackWrite
+ //
+ virtual
+ VOID
+ STDMETHODCALLTYPE
+ OnWrite(
+ _In_ IWDFIoQueue *pWdfQueue,
+ _In_ IWDFIoRequest *pWdfRequest,
+ _In_ SIZE_T NumOfBytesToWrite
+ );
+
+ // IQueueCallbackRead
+ //
+ virtual
+ VOID
+ STDMETHODCALLTYPE
+ OnRead(
+ _In_ IWDFIoQueue *pWdfQueue,
+ _In_ IWDFIoRequest *pWdfRequest,
+ _In_ SIZE_T NumOfBytesToRead
+ );
+};
diff --git a/serial/VirtualSerial/ringbuffer.cpp b/serial/VirtualSerial/ringbuffer.cpp
new file mode 100644
index 00000000..fbbbd6b8
--- /dev/null
+++ b/serial/VirtualSerial/ringbuffer.cpp
@@ -0,0 +1,258 @@
+#include "internal.h"
+
+CRingBuffer::CRingBuffer(
+ VOID
+ ) : m_Size(0),
+ m_Base(NULL),
+ m_End(NULL),
+ m_Head(NULL),
+ m_Tail(NULL)
+{
+ return;
+}
+
+CRingBuffer::~CRingBuffer(
+ VOID
+ )
+{
+ if (m_Base)
+ {
+ delete[] m_Base;
+ }
+}
+
+HRESULT
+CRingBuffer::Initialize(
+ _In_ SIZE_T BufferSize
+ )
+{
+ HRESULT hr = S_OK;
+ PBYTE buffer = NULL;
+
+ if (0 == BufferSize)
+ {
+ hr = E_INVALIDARG;
+ }
+
+ //
+ // Allocate the buffer.
+ //
+ if (SUCCEEDED(hr))
+ {
+ buffer = new BYTE[BufferSize];
+ if (buffer == NULL)
+ {
+ hr = E_OUTOFMEMORY;
+ }
+ }
+
+ //
+ // Initialize the ring buffer pointers.
+ //
+ if (SUCCEEDED(hr))
+ {
+ m_Size = BufferSize;
+ m_Base = buffer;
+ m_End = buffer + BufferSize;
+ m_Head = buffer;
+ m_Tail = buffer;
+ }
+
+ if (FAILED(hr))
+ {
+ if (buffer)
+ {
+ delete[] buffer;
+ m_Base = NULL;
+ }
+ }
+
+ return hr;
+}
+
+HRESULT
+CRingBuffer::Write(
+ _In_reads_bytes_(DataSize) PBYTE Data,
+ _In_ SIZE_T DataSize
+ )
+{
+ SIZE_T availableSpace;
+ SIZE_T bytesToCopy;
+ SIZE_T spaceFromCurrToEnd;
+
+ WUDF_TEST_DRIVER_ASSERT(Data && (0 != DataSize));
+
+ if (m_Tail >= m_End)
+ {
+ return E_UNEXPECTED;
+ }
+
+ //
+ // Get the amount of space available in the buffer
+ //
+ GetAvailableSpace(&availableSpace);
+
+ //
+ // If there is not enough space to fit in all the data passed in by the
+ // caller then copy as much as possible and throw away the rest
+ //
+ if (availableSpace < DataSize)
+ {
+ bytesToCopy = availableSpace;
+ }
+ else
+ {
+ bytesToCopy = DataSize;
+ }
+
+ if (bytesToCopy)
+ {
+ //
+ // The buffer has some space at least
+ //
+ if ((m_Tail+bytesToCopy) > m_End)
+ {
+ //
+ // The data being written will wrap around the end of the buffer.
+ // So the copy has to be done in two steps -
+ // * X bytes from current position to end of the buffer
+ // * the remaining (bytesToCopy - X) from the start of the buffer
+ //
+
+ //
+ // The first step of the copy ...
+ //
+ spaceFromCurrToEnd = m_End - m_Tail;
+ CopyMemory(m_Tail, Data, spaceFromCurrToEnd);
+ Data += spaceFromCurrToEnd;
+ bytesToCopy -= spaceFromCurrToEnd;
+
+ //
+ // The second step of the copy ...
+ //
+ CopyMemory(m_Base, Data, bytesToCopy);
+
+ //
+ // Advance the tail pointer
+ //
+ m_Tail = m_Base + bytesToCopy;
+ }
+ else
+ {
+ //
+ // Data does NOT wrap around the end of the buffer. Just copy it
+ // over in a single step
+ //
+ CopyMemory(m_Tail, Data, bytesToCopy);
+
+ //
+ // Advance the tail pointer
+ //
+ m_Tail += bytesToCopy;
+ if (m_Tail == m_End)
+ {
+ //
+ // We have exactly reached the end of the buffer. The next
+ // write should wrap around and start from the beginning.
+ //
+ m_Tail = m_Base;
+ }
+ }
+
+ WUDF_TEST_DRIVER_ASSERT(m_Tail < m_End);
+ }
+
+ return S_OK;
+}
+
+HRESULT
+CRingBuffer::Read(
+ _Out_writes_bytes_to_(DataSize, *BytesCopied) PBYTE Data,
+ _In_ SIZE_T DataSize,
+ _Out_ SIZE_T *BytesCopied
+ )
+{
+ SIZE_T availableData;
+ SIZE_T dataFromCurrToEnd;
+
+ WUDF_TEST_DRIVER_ASSERT(Data && (DataSize != 0));
+
+ if (m_Head >= m_End)
+ {
+ return E_UNEXPECTED;
+ }
+
+ //
+ // Get the amount of data available in the buffer
+ //
+ GetAvailableData(&availableData);
+
+ if (availableData == 0)
+ {
+ *BytesCopied = 0;
+ return S_OK;
+ }
+
+ if (DataSize > availableData)
+ {
+ DataSize = availableData;
+ }
+
+ *BytesCopied = DataSize;
+
+ if ((m_Head + DataSize) > m_End)
+ {
+ //
+ // The data requested by the caller is wrapped around the end of the
+ // buffer. So we'll do the copy in two steps -
+ // * Copy X bytes from the current position to the end buffer into
+ // the caller's buffer
+ // * Copy (DataSize - X) bytes from the beginning to the buffer into
+ // the caller's buffer
+ //
+
+ //
+ // The first step of the copy ...
+ //
+ dataFromCurrToEnd = m_End - m_Head;
+ CopyMemory(Data, m_Head, dataFromCurrToEnd);
+ Data += dataFromCurrToEnd;
+ DataSize -= dataFromCurrToEnd;
+
+ //
+ // The second step of the copy ...
+ //
+ CopyMemory(Data, m_Base, DataSize);
+
+ //
+ // Advance the head pointer
+ //
+ m_Head = m_Base + DataSize;
+ }
+ else
+ {
+ //
+ // The data in the buffer is NOT wrapped around the end of the buffer.
+ // Simply copy the data over to the caller's buffer in a single step.
+ //
+ CopyMemory(Data, m_Head, DataSize);
+
+ //
+ // Advance the head pointer
+ //
+ m_Head += DataSize;
+ if (m_Head == m_End)
+ {
+ //
+ // We have exactly reached the end of the buffer. The next
+ // read should wrap around and start from the beginning.
+ //
+ m_Head = m_Base;
+ }
+ }
+
+ WUDF_TEST_DRIVER_ASSERT(m_Head < m_End);
+
+ return S_OK;
+}
+
diff --git a/serial/VirtualSerial/ringbuffer.h b/serial/VirtualSerial/ringbuffer.h
new file mode 100644
index 00000000..130055d9
--- /dev/null
+++ b/serial/VirtualSerial/ringbuffer.h
@@ -0,0 +1,223 @@
+#pragma once
+
+#include "internal.h"
+
+class CRingBuffer
+{
+private:
+
+ //
+ // Private local variables.
+ //
+
+ //
+ // The size in bytes of the ring buffer.
+ //
+ SIZE_T m_Size;
+
+ //
+ // A pointer to the base of the ring buffer.
+ //
+ PBYTE m_Base;
+
+ //
+ // A pointer to the byte beyond the end of the ring buffer. Used for
+ // quick comparisons when determining if we need to wrap.
+ //
+ PBYTE m_End;
+
+ //
+ // A pointer to the current read point in the ring buffer.
+ //
+ // Updates to this are not protected by any lock. This is different from
+ // the write pointer, which is protected by the "pending read pointer"
+ // lock. The reason for this difference is that in this driver, we do not
+ // keep write requests pending. If there is not enough space to write all
+ // the data that was requested, we write as much as we can and drop the
+ // rest (lossy data transfer).
+ //
+ // If we had multiple threads modifying this pointer, then that would
+ // provide yet another reason for protecting updates to the pointer using a
+ // lock. However, in this driver, at any given time we have only one thread
+ // that modifies this pointer (the thread that runs the read callback).
+ // This is true because we use a sequential queue for read requests. If we
+ // were to change our read queue to be a parallel queue, this would no
+ // longer be true.
+ //
+ //
+ PBYTE m_Head;
+
+ //
+ // A pointer to the current write point in the ring buffer.
+ //
+ // Updates to this pointer are protected by the "pending read pointer
+ // lock", because we do not want a consumer thread to mark a read request
+ // as pending while we are in the process of writing data to the buffer.
+ // The reason is that the write that we are currently performing might
+ // actually supply enough data to satisfy the read request, in which case
+ // it should not be marked pending at all.
+ // If the read request were to be marked pending in the situation described
+ // above, then we would need some trigger to later retrieve the request and
+ // complete it. In our driver, arrival of data is the only event that can
+ // trigger this. So if no more data arrives, the request will remain
+ // pending forever, even though there is enough data in the buffer to
+ // complete it. Hence we do not keep a read request pending in situations
+ // where the read buffer contains enough data to satisfy it.
+ //
+ // If we had multiple threads modifying this pointer, then that would
+ // provide yet another reason for protecting updates to the pointer using a
+ // lock. However, in this driver, at any given time we have only one thread
+ // that modifies this pointer (the thread that runs the write callback).
+ // This is true because we use a sequential queue for write requests. If we
+ // were to change our write queue to be a parallel queue, this would no
+ // longer be true.
+ //
+ PBYTE m_Tail;
+
+private:
+
+ //
+ // Private Internal Methods.
+ //
+ void
+ GetAvailableSpace(
+ _Out_ SIZE_T *AvailableSpace
+ )
+ {
+ WUDF_TEST_DRIVER_ASSERT(AvailableSpace);
+
+ PBYTE headSnapshot = NULL;
+ PBYTE tailSnapshot = NULL;
+ PBYTE tailPlusOne = NULL;
+
+ //
+ // Take a snapshot of the head and tail pointers. We will compute the
+ // available space based on this snapshot. This is safe to do in a
+ // single-producer, single-consumer model, because -
+ // * A producer will call GetAvailableSpace() to determine whether
+ // there is enough space to write the data it is trying to write.
+ // The only other thread that could modify the amount of space
+ // available is the consumer thread, which can only increase the
+ // amount of space available. Hence it is safe for the producer
+ // to write based on this snapshot.
+ // * A consumer thread will call GetAvailableSpace() to determine
+ // whether there is enough data in the buffer for it to read.
+ // (Available data = Buffer size - Available space). The only
+ // other thread that could modify the amount of space available
+ // is the producer thread, which can only decrease the amount of
+ // space available (thereby increasing the amount of data
+ // available. Hence it is safe for the consumer to read based on
+ // this snapshot.
+ //
+ headSnapshot = m_Head;
+ tailSnapshot = m_Tail;
+
+ //
+ // In order to distinguish between a full buffer and an empty buffer,
+ // we always leave the last byte of the buffer unused. So, an empty
+ // buffer is denoted by -
+ // tail == head
+ // ... and a full buffer is denoted by -
+ // (tail+1) == head
+ //
+ tailPlusOne = ((tailSnapshot+1) == m_End) ? m_Base : (tailSnapshot+1);
+
+ if (tailPlusOne == headSnapshot)
+ {
+ //
+ // Buffer full
+ //
+ *AvailableSpace = 0;
+ }
+ else if (tailSnapshot == headSnapshot)
+ {
+ //
+ // Buffer empty
+ // The -1 in the computation below is to account for the fact that
+ // we always leave the last byte of the ring buffer unused in order
+ // to distinguish between an empty buffer and a full buffer.
+ //
+ *AvailableSpace = m_Size - 1;
+ }
+ else
+ {
+ if (tailSnapshot > headSnapshot)
+ {
+ //
+ // Data has not wrapped around the end of the buffer
+ // The -1 in the computation below is to account for the fact
+ // that we always leave the last byte of the ring buffer unused
+ // in order to distinguish between an empty buffer and a full
+ // buffer.
+ //
+ *AvailableSpace = m_Size - (tailSnapshot - headSnapshot) - 1;
+ }
+ else
+ {
+ //
+ // Data has wrapped around the end of the buffer
+ // The -1 in the computation below is to account for the fact
+ // that we always leave the last byte of the ring buffer unused
+ // in order to distinguish between an empty buffer and a full
+ // buffer.
+ //
+ *AvailableSpace = (headSnapshot - tailSnapshot) - 1;
+ }
+ }
+
+ return;
+ }
+
+public:
+
+ //
+ // Public Internal methods.
+ //
+
+ CRingBuffer(
+ VOID
+ );
+
+ ~CRingBuffer(
+ VOID
+ );
+
+ HRESULT
+ Initialize(
+ _In_ SIZE_T BufferSize
+ );
+
+ HRESULT
+ Write(
+ _In_reads_bytes_(DataSize) PBYTE Data,
+ _In_ SIZE_T DataSize
+ );
+
+ HRESULT
+ Read(
+ _Out_writes_bytes_to_(DataSize, *BytesCopied) PBYTE Data,
+ _In_ SIZE_T DataSize,
+ _Out_ SIZE_T *BytesCopied
+ );
+
+ void
+ GetAvailableData(
+ _Out_ SIZE_T *AvailableData
+ )
+ {
+ SIZE_T availableSpace;
+
+ WUDF_TEST_DRIVER_ASSERT(AvailableData);
+
+ GetAvailableSpace(&availableSpace);
+
+ //
+ // The -1 in the arithmetic below accounts for the fact that we always
+ // keep 1 byte of the ring buffer unused in order to distinguish
+ // between a full buffer and an empty buffer.
+ //
+ *AvailableData = m_Size - availableSpace - 1;
+
+ return;
+ }
+};
diff --git a/serial/VirtualSerial/serial.h b/serial/VirtualSerial/serial.h
new file mode 100644
index 00000000..0ae91186
--- /dev/null
+++ b/serial/VirtualSerial/serial.h
@@ -0,0 +1,48 @@
+/*++
+
+Module Name :
+
+ serial.h
+
+Abstract:
+
+ Type definitions and data for the serial port driver
+
+Author:
+
+
+--*/
+
+#pragma once
+
+#define SYMBOLIC_NAME_LENGTH 32
+#define SYMBOLIC_LINK_NAME_PREFIX L"\\DosDevices\\Global\\"
+
+//
+// This defines the bit used to control whether the device is sending
+// a break. When this bit is set the device is sending a space (logic 0).
+//
+// Most protocols will assume that this is a hangup.
+//
+#define SERIAL_LCR_BREAK 0x40
+
+//
+// These defines are used to set the line control register.
+//
+#define SERIAL_5_DATA ((UCHAR)0x00)
+#define SERIAL_6_DATA ((UCHAR)0x01)
+#define SERIAL_7_DATA ((UCHAR)0x02)
+#define SERIAL_8_DATA ((UCHAR)0x03)
+#define SERIAL_DATA_MASK ((UCHAR)0x03)
+
+#define SERIAL_1_STOP ((UCHAR)0x00)
+#define SERIAL_1_5_STOP ((UCHAR)0x04) // Only valid for 5 data bits
+#define SERIAL_2_STOP ((UCHAR)0x04) // Not valid for 5 data bits
+#define SERIAL_STOP_MASK ((UCHAR)0x04)
+
+#define SERIAL_NONE_PARITY ((UCHAR)0x00)
+#define SERIAL_ODD_PARITY ((UCHAR)0x08)
+#define SERIAL_EVEN_PARITY ((UCHAR)0x18)
+#define SERIAL_MARK_PARITY ((UCHAR)0x28)
+#define SERIAL_SPACE_PARITY ((UCHAR)0x38)
+#define SERIAL_PARITY_MASK ((UCHAR)0x38)
diff --git a/serial/serenum/ReadMe.md b/serial/serenum/ReadMe.md
new file mode 100644
index 00000000..afb6916f
--- /dev/null
+++ b/serial/serenum/ReadMe.md
@@ -0,0 +1,39 @@
+Serenum sample
+==============
+
+Serenum enumerates Plug-n-Play RS-232 devices that are compliant with the current revision of Plug and Play External COM Device. It loads as an upper filter driver to many different RS-232 device drivers that are compliant with its requirements and performs this service for them.
+
+Serenum implements the Serenum service; its executable image is serenum.sys.
+
+Serenum is an upper-level device filter driver that is used with a serial port function driver to enumerate the following types of devices that are connected to an RS-232 port:
+
+- Plug and Play serial devices that comply with Plug and Play External COM Device Specification, Version 1.00, February 28, 1995.
+- Pointer devices that comply with legacy mouse detection in Windows.
+
+The combined operation of Serial and Serenum provides the function of a Plug and Play bus driver for an RS-232 port. Serenum supports Plug and Play and power management.
+
+Windows provides Serenum to support Serial and other serial port function drivers that need to enumerate an RS-232 port. Hardware vendors do not have to create their own enumerator for RS-232 ports. For example, a device driver can use Serenum to enumerate the devices that are attached to the individual RS-232 ports on a multiport device.
+
+<table>
+<colgroup>
+<col width="50%" />
+<col width="50%" />
+</colgroup>
+<thead>
+<tr class="header">
+<th align="left">File manifest
+Description</th>
+</tr>
+</thead>
+<tbody>
+<tr class="odd">
+<td align="left">Enum.c
+Functions that enumerate external serial devices—the main purpose of this driver</td>
+<td align="left">Pnp.c
+Plug and Play support code</td>
+</tr>
+</tbody>
+</table>
+
+For more information, see [Features of Serial and Serenum](http://msdn.microsoft.com/en-us/library/windows/hardware/ff546505).
+
diff --git a/serial/serenum/SerEnum_sample.vcxproj b/serial/serenum/SerEnum_sample.vcxproj
new file mode 100644
index 00000000..dfb24ba2
--- /dev/null
+++ b/serial/serenum/SerEnum_sample.vcxproj
@@ -0,0 +1,219 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{E52D8BEE-C6BE-4357-902C-C6DB9C0EF34F}</ProjectGuid>
+ <RootNamespace>$(MSBuildProjectName)</RootNamespace>
+ <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration>
+ <Platform Condition="'$(Platform)' == ''">Win32</Platform>
+ <SampleGuid>{CA7D7702-DAFD-4229-BF86-20E446AECF97}</SampleGuid>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>False</UseDebugLibraries>
+ <DriverTargetPlatform>Desktop</DriverTargetPlatform>
+ <DriverType>WDM</DriverType>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+ <ConfigurationType>Driver</ConfigurationType>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>True</UseDebugLibraries>
+ <DriverTargetPlatform>Desktop</DriverTargetPlatform>
+ <DriverType>WDM</DriverType>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+ <ConfigurationType>Driver</ConfigurationType>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>False</UseDebugLibraries>
+ <DriverTargetPlatform>Desktop</DriverTargetPlatform>
+ <DriverType>WDM</DriverType>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+ <ConfigurationType>Driver</ConfigurationType>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>True</UseDebugLibraries>
+ <DriverTargetPlatform>Desktop</DriverTargetPlatform>
+ <DriverType>WDM</DriverType>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+ <ConfigurationType>Driver</ConfigurationType>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <PropertyGroup>
+ <OutDir>$(IntDir)</OutDir>
+ </PropertyGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ItemGroup Label="WrappedTaskItems" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <TargetName>SerEnum_sample</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <TargetName>SerEnum_sample</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <TargetName>SerEnum_sample</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <TargetName>SerEnum_sample</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);/wd4996</PreprocessorDefinitions>
+ </ClCompile>
+ <Midl>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);/wd4996</PreprocessorDefinitions>
+ </Midl>
+ <ResourceCompile>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);/wd4996</PreprocessorDefinitions>
+ </ResourceCompile>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);/wd4996</PreprocessorDefinitions>
+ </ClCompile>
+ <Midl>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);/wd4996</PreprocessorDefinitions>
+ </Midl>
+ <ResourceCompile>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);/wd4996</PreprocessorDefinitions>
+ </ResourceCompile>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);/wd4996</PreprocessorDefinitions>
+ </ClCompile>
+ <Midl>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);/wd4996</PreprocessorDefinitions>
+ </Midl>
+ <ResourceCompile>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);/wd4996</PreprocessorDefinitions>
+ </ResourceCompile>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);/wd4996</PreprocessorDefinitions>
+ </ClCompile>
+ <Midl>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);/wd4996</PreprocessorDefinitions>
+ </Midl>
+ <ResourceCompile>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);/wd4996</PreprocessorDefinitions>
+ </ResourceCompile>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="enum.c">
+ <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreCompiledHeaderFile>pch.h</PreCompiledHeaderFile>
+ <PreCompiledHeader>Use</PreCompiledHeader>
+ <PreCompiledHeaderOutputFile>$(IntDir)\pch.pch</PreCompiledHeaderOutputFile>
+ </ClCompile>
+ <ClCompile Include="log.c">
+ <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreCompiledHeaderFile>pch.h</PreCompiledHeaderFile>
+ <PreCompiledHeader>Use</PreCompiledHeader>
+ <PreCompiledHeaderOutputFile>$(IntDir)\pch.pch</PreCompiledHeaderOutputFile>
+ </ClCompile>
+ <ClCompile Include="pnp.c">
+ <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreCompiledHeaderFile>pch.h</PreCompiledHeaderFile>
+ <PreCompiledHeader>Use</PreCompiledHeader>
+ <PreCompiledHeaderOutputFile>$(IntDir)\pch.pch</PreCompiledHeaderOutputFile>
+ </ClCompile>
+ <ClCompile Include="power.c">
+ <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreCompiledHeaderFile>pch.h</PreCompiledHeaderFile>
+ <PreCompiledHeader>Use</PreCompiledHeader>
+ <PreCompiledHeaderOutputFile>$(IntDir)\pch.pch</PreCompiledHeaderOutputFile>
+ </ClCompile>
+ <ClCompile Include="serenum.c">
+ <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreCompiledHeaderFile>pch.h</PreCompiledHeaderFile>
+ <PreCompiledHeader>Use</PreCompiledHeader>
+ <PreCompiledHeaderOutputFile>$(IntDir)\pch.pch</PreCompiledHeaderOutputFile>
+ </ClCompile>
+ <ClCompile Include="string.c">
+ <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreCompiledHeaderFile>pch.h</PreCompiledHeaderFile>
+ <PreCompiledHeader>Use</PreCompiledHeader>
+ <PreCompiledHeaderOutputFile>$(IntDir)\pch.pch</PreCompiledHeaderOutputFile>
+ </ClCompile>
+ <ResourceCompile Include="serenum.rc" />
+ </ItemGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ </ClCompile>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ </ClCompile>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ </ClCompile>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ </ClCompile>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="pchsrc.c">
+ <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreCompiledHeaderFile>pch.h</PreCompiledHeaderFile>
+ <PreCompiledHeader>Create</PreCompiledHeader>
+ <PreCompiledHeaderOutputFile>$(IntDir)\pch.pch</PreCompiledHeaderOutputFile>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <Inf Exclude="@(Inf)" Include="*.inf" />
+ <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" />
+ <FilesToPackage Include="@(Inf->'%(CopyOutput)')" Condition="'@(Inf)'!=''" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Exclude="@(None)" Include="*.txt;*.htm;*.html" />
+ <None Exclude="@(None)" Include="*.ico;*.cur;*.bmp;*.dlg;*.rct;*.gif;*.jpg;*.jpeg;*.wav;*.jpe;*.tiff;*.tif;*.png;*.rc2" />
+ <None Exclude="@(None)" Include="*.def;*.bat;*.hpj;*.asmx" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+</Project> \ No newline at end of file
diff --git a/serial/serenum/SerEnum_sample.vcxproj.Filters b/serial/serenum/SerEnum_sample.vcxproj.Filters
new file mode 100644
index 00000000..acc1f18d
--- /dev/null
+++ b/serial/serenum/SerEnum_sample.vcxproj.Filters
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions>
+ <UniqueIdentifier>{5046CCD8-3045-41D7-BCAE-587CADA81725}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files">
+ <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
+ <UniqueIdentifier>{976EA0BF-ABEF-4B5E-9C22-13AEAACBE66B}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Resource Files">
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions>
+ <UniqueIdentifier>{D81BDD2E-183B-4A6D-B1A6-A6D4DE701F20}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Driver Files">
+ <Extensions>inf;inv;inx;mof;mc;</Extensions>
+ <UniqueIdentifier>{35AA0A20-80E1-4672-B792-9FEECE8B736E}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="enum.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="log.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="pchsrc.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="pnp.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="power.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="serenum.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="string.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="serenum.rc">
+ <Filter>Resource Files</Filter>
+ </ResourceCompile>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/serial/serenum/enum.c b/serial/serenum/enum.c
new file mode 100644
index 00000000..feb6f4af
--- /dev/null
+++ b/serial/serenum/enum.c
@@ -0,0 +1,1834 @@
+/*++
+Copyright (c) 1997 Microsoft Corporation
+
+Module Name:
+
+ ENUM.C
+
+Abstract:
+
+ This module contains the enumeration code needed to figure out
+ whether or not a device is attached to the serial port. If there
+ is one, it will obtain the PNP COM ID (if the device is PNP) and
+ parse out the relevant fields.
+
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+
+--*/
+
+
+#include "pch.h"
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGESENM, SerenumValidateID)
+#pragma alloc_text(PAGESENM, SerenumDoEnumProtocol)
+#pragma alloc_text(PAGESENM, SerenumCheckForLegacyDevice)
+#pragma alloc_text(PAGESENM, SerenumScanOtherIdForMouse)
+#pragma alloc_text(PAGESENM, Serenum_ReenumerateDevices)
+#pragma alloc_text(PAGESENM, Serenum_IoSyncReq)
+#pragma alloc_text(PAGESENM, Serenum_IoSyncReqWithIrp)
+#pragma alloc_text(PAGESENM, Serenum_IoSyncIoctlEx)
+#pragma alloc_text(PAGESENM, Serenum_ReadSerialPort)
+#pragma alloc_text(PAGESENM, Serenum_Wait)
+#pragma alloc_text(PAGESENM, SerenumReleaseThreadReference)
+
+//#pragma alloc_text (PAGE, Serenum_GetRegistryKeyValue)
+#endif
+
+#if !defined(__isascii)
+#define __isascii(_c) ( (unsigned)(_c) < 0x80 )
+#endif // !defined(__isascii)
+
+// disable warnings
+
+#if _MSC_VER >= 1200
+#pragma warning(push)
+#endif
+
+#pragma warning(disable:4127) // conditional expression is constant
+
+void
+SerenumScanOtherIdForMouse(
+ _In_reads_(BufLen) PCHAR PBuffer,
+ _In_ ULONG BufLen,
+ _Outptr_result_buffer_maybenull_(*PmouseIdLen) PCHAR *PpMouseId,
+ _Out_ ULONG *PmouseIdLen
+ )
+/*++
+
+Routine Description:
+
+ This routines a PnP packet for a mouse ID up to the first PnP delimiter
+ (i.e, '(').
+
+Arguments:
+
+ PBuffer - Pointer to the buffer to scan
+
+ BufLen - Length of the buffer in bytes
+
+ PpMouseId - Pointer to the pointer to the mouse ID (this will be set
+ to point to the location in the buffer where the mouse ID
+ was found)
+
+Return value:
+
+ void
+
+--*/
+{
+ PAGED_CODE();
+
+ *PpMouseId = PBuffer;
+
+ while (BufLen--) {
+ if (**PpMouseId == 'M' || **PpMouseId == 'B') {
+ *PmouseIdLen = BufLen+1;
+ return;
+ } else if (**PpMouseId == '(' || **PpMouseId == ('(' - 0x20)) {
+ *PmouseIdLen = 0;
+ *PpMouseId = NULL;
+ return;
+ }
+ (*PpMouseId)++;
+ }
+ *PmouseIdLen = 0;
+ *PpMouseId = NULL;
+}
+
+#if DBG
+VOID SerenumHexDump(PUCHAR PBuf, ULONG NBytes)
+/*++
+
+Routine Description:
+
+ Hex dump a buffer with NPerRow chars per row output
+
+Arguments:
+
+ PBuf - Pointer to the buffer to dump
+
+ NBytes - Length of the buffer in bytes
+
+Return value:
+
+ VOID
+
+--*/
+{
+ const ULONG NPerRow = 20;
+
+ ULONG dmpi;
+ ULONG col;
+ UCHAR c;
+ ULONG LoopCount = 1;
+ ULONG dividend = NBytes / NPerRow;
+ ULONG remainder = NBytes % NPerRow;
+ ULONG nHexChars = NPerRow;
+ ULONG nSpaces = 1;
+
+ DbgPrint("SERENUM: Raw Data Packet on probe\n");
+
+ if (remainder) {
+ LoopCount++;
+ }
+
+ for (dmpi = 0; dmpi < (dividend + 1); dmpi++) {
+ DbgPrint("-------: ");
+
+ for (col = 0; col < nHexChars; col++) {
+ DbgPrint("%02x ", (unsigned char)PBuf[dmpi * NPerRow + col]);
+ }
+
+ for (col = 0; col < nSpaces; col++) {
+ DbgPrint(" ");
+ }
+
+ for (col = 0; col < nHexChars; col++){
+ c = PBuf[dmpi * NPerRow + col];
+
+ if (__isascii(c) && (c > ' ')){
+ DbgPrint("%c", c);
+ }else{
+ DbgPrint(".");
+ }
+ }
+
+ DbgPrint("\n");
+
+ //
+ // If this is the last one, then we have less that NPerRow to dump
+ //
+
+ if (dmpi == dividend) {
+ if (remainder == 0) {
+ //
+ // This was an even multiple -- we're done
+ //
+
+ break; // for (dmpi)
+ } else {
+ nHexChars = remainder;
+ nSpaces = NPerRow - nHexChars;
+ }
+ }
+ }
+}
+#endif // DBG
+
+NTSTATUS
+SerenumDoEnumProtocol(
+ PFDO_DEVICE_DATA PFdoData,
+ _Outptr_result_buffer_(*PNBytes) PUCHAR *PpBuf,
+ PUSHORT PNBytes,
+ PBOOLEAN PDSRMissing)
+{
+ IO_STATUS_BLOCK ioStatusBlock;
+ ULONG i;
+ ULONG bitMask;
+ KEVENT event;
+ KTIMER timer;
+ NTSTATUS status;
+ PUCHAR pReadBuf;
+ USHORT nRead;
+ PDEVICE_OBJECT pDevStack = PFdoData->TopOfStack;
+ SERIAL_BAUD_RATE baudRate;
+ SERIAL_LINE_CONTROL lineControl;
+
+#if DBG
+#define PERFCNT 1
+#endif
+
+#if defined(PERFCNT)
+ LARGE_INTEGER perfFreq;
+ LARGE_INTEGER stPerfCnt, endPerfCnt;
+ LONG diff;
+#endif
+
+ LARGE_INTEGER DefaultWait;
+
+ PAGED_CODE();
+
+ KeInitializeEvent(&event, NotificationEvent, FALSE);
+ KeInitializeTimer(&timer);
+
+#if defined(PERFCNT)
+ perfFreq.QuadPart = (LONGLONG) 0;
+#endif
+ DefaultWait.QuadPart = (LONGLONG) -(SERENUM_DEFAULT_WAIT);
+ *PpBuf = NULL;
+ pReadBuf = NULL;
+ nRead = 0;
+ *PDSRMissing = FALSE;
+
+ LOGENTRY(LOG_ENUM, 'SDEP', PFdoData, PpBuf, PDSRMissing);
+
+
+ pReadBuf = ExAllocatePoolWithTag(NonPagedPoolNx, MAX_DEVNODE_NAME + sizeof(CHAR) ,SERENUM_POOL_TAG);
+
+ if (pReadBuf == NULL) {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ LOGENTRY(LOG_ENUM, 'SDE1', PFdoData, status, 0);
+ goto ProtocolDone;
+ }
+
+ *(pReadBuf + MAX_DEVNODE_NAME) = 0;
+
+ //
+ // Set DTR
+ //
+
+ Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE, ("Setting DTR...\n"));
+
+ status = Serenum_IoSyncIoctl(IOCTL_SERIAL_SET_DTR, FALSE, pDevStack, &event);
+
+ if (!NT_SUCCESS(status)) {
+ LOGENTRY(LOG_ENUM, 'SDE2', PFdoData, status, 0);
+ goto ProtocolDone;
+ }
+
+ //
+ // Clear RTS
+ //
+ Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE, ("Clearing RTS...\n"));
+
+ status = Serenum_IoSyncIoctl(IOCTL_SERIAL_CLR_RTS, FALSE, pDevStack, &event);
+
+ if (!NT_SUCCESS(status)) {
+ LOGENTRY(LOG_ENUM, 'SDE3', PFdoData, status, 0);
+ goto ProtocolDone;
+ }
+
+ //
+ // Wait for the default timeout period
+ //
+
+#if defined(PERFCNT)
+ stPerfCnt = KeQueryPerformanceCounter(&perfFreq);
+#endif
+
+ status = Serenum_Wait(&timer, DefaultWait);
+
+#if defined(PERFCNT)
+ endPerfCnt = KeQueryPerformanceCounter(NULL);
+ diff = (LONG)(endPerfCnt.QuadPart - stPerfCnt.QuadPart);
+ diff *= 1000;
+ diff /= (LONG)perfFreq.QuadPart;
+
+ LOGENTRY(LOG_ENUM, 'SDT0', PFdoData, diff, 0);
+#endif
+
+ if (!NT_SUCCESS(status)) {
+ Serenum_KdPrint(PFdoData, SER_DBG_SS_ERROR,
+ ("Timer failed with status %x\n", status ));
+ LOGENTRY(LOG_ENUM, 'SDE4', PFdoData, status, 0);
+ goto ProtocolDone;
+ }
+
+ Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE, ("Checking DSR...\n"));
+
+ status = Serenum_IoSyncIoctlEx(IOCTL_SERIAL_GET_MODEMSTATUS, FALSE,
+ pDevStack, &event, NULL, 0, &bitMask,
+ sizeof(ULONG));
+
+ if (!NT_SUCCESS(status)) {
+ LOGENTRY(LOG_ENUM, 'SDE5', PFdoData, status, 0);
+ goto ProtocolDone;
+ }
+
+ //
+ // If DSR is not set, then a legacy device (like a mouse) may be attached --
+ // they are not required to assert DSR when they are present and ready.
+ //
+
+ if ((SERIAL_DSR_STATE & bitMask) == 0) {
+ Serenum_KdPrint (PFdoData, SER_DBG_SS_TRACE,
+ ("No PNP device available - DSR not set.\n"));
+ *PDSRMissing = TRUE;
+ LOGENTRY(LOG_ENUM, 'SDND', PFdoData, 0, 0);
+ }
+
+ //
+ // Setup the serial port for 1200 bits/s, 7 data bits,
+ // no parity, one stop bit
+ //
+ Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE, ("Setting baud rate to 1200..."
+ "\n"));
+ baudRate.BaudRate = 1200;
+ status = Serenum_IoSyncIoctlEx(IOCTL_SERIAL_SET_BAUD_RATE, FALSE, pDevStack,
+ &event, &baudRate, sizeof(SERIAL_BAUD_RATE),
+ NULL, 0);
+ if (!NT_SUCCESS(status)) {
+ LOGENTRY(LOG_ENUM, 'SDE6', PFdoData, status, 0);
+ goto ProtocolDone;
+ }
+
+ Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE,
+ ("Setting the line control...\n"));
+
+ lineControl.StopBits = STOP_BIT_1;
+ lineControl.Parity = NO_PARITY;
+ lineControl.WordLength = 7;
+
+ status = Serenum_IoSyncIoctlEx(IOCTL_SERIAL_SET_LINE_CONTROL, FALSE,
+ pDevStack, &event, &lineControl,
+ sizeof(SERIAL_LINE_CONTROL), NULL, 0);
+
+ if (!NT_SUCCESS(status)) {
+ LOGENTRY(LOG_ENUM, 'SDE7', PFdoData, status, 0);
+ goto ProtocolDone;
+ }
+
+
+ //
+ // loop twice
+ // The first iteration is for reading the PNP ID string from modems
+ // and mice.
+ // The second iteration is for other devices.
+ //
+ for (i = 0; i < 2; i++) {
+ //
+ // Purge the buffers before reading
+ //
+
+ LOGENTRY(LOG_ENUM, 'SDEI', PFdoData, i, 0);
+
+ Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE, ("Purging all buffers...\n"));
+
+ bitMask = SERIAL_PURGE_RXCLEAR;
+
+ status = Serenum_IoSyncIoctlEx(IOCTL_SERIAL_PURGE, FALSE, pDevStack,
+ &event, &bitMask, sizeof(ULONG), NULL, 0);
+
+ if (!NT_SUCCESS(status)) {
+ LOGENTRY(LOG_ENUM, 'SDE8', PFdoData, status, 0);
+ break;
+ }
+
+ //
+ // Clear DTR
+ //
+ Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE, ("Clearing DTR...\n"));
+
+ status = Serenum_IoSyncIoctl(IOCTL_SERIAL_CLR_DTR, FALSE, pDevStack,
+ &event);
+
+ if (!NT_SUCCESS(status)) {
+ LOGENTRY(LOG_ENUM, 'SDE9', PFdoData, status, 0);
+ break;
+ }
+
+ //
+ // Clear RTS
+ //
+ Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE, ("Clearing RTS...\n"));
+
+ status = Serenum_IoSyncIoctl(IOCTL_SERIAL_CLR_RTS, FALSE, pDevStack,
+ &event);
+
+ if (!NT_SUCCESS(status)) {
+ LOGENTRY(LOG_ENUM, 'SDEA', PFdoData, status, 0);
+ break;
+ }
+
+ //
+ // Set a timer for 200 ms
+ //
+
+ Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE, ("Waiting...\n"));
+
+#if defined(PERFCNT)
+ stPerfCnt = KeQueryPerformanceCounter(&perfFreq);
+#endif
+
+ status = Serenum_Wait(&timer, DefaultWait);
+
+ if (!NT_SUCCESS(status)) {
+ Serenum_KdPrint(PFdoData, SER_DBG_SS_ERROR,
+ ("Timer failed with status %x\n", status ));
+ LOGENTRY(LOG_ENUM, 'SDEB', PFdoData, status, 0);
+ break;
+ }
+
+ //
+ // set DTR
+ //
+
+ Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE, ("Setting DTR...\n"));
+
+ status = Serenum_IoSyncIoctl(IOCTL_SERIAL_SET_DTR, FALSE, pDevStack,
+ &event);
+
+ if (!NT_SUCCESS(status)) {
+ LOGENTRY(LOG_ENUM, 'SDEC', PFdoData, status, 0);
+ break;
+ }
+
+
+#if defined(PERFCNT)
+ endPerfCnt = KeQueryPerformanceCounter(NULL);
+ diff = (LONG)(endPerfCnt.QuadPart - stPerfCnt.QuadPart);
+ diff *= 1000;
+ diff /= (LONG)perfFreq.QuadPart;
+
+ LOGENTRY(LOG_ENUM, 'SDT1', PFdoData, diff, 0);
+#endif
+
+ //
+ // First iteration is for modems
+ // Therefore wait for 200 ms as per protocol for getting PNP string out
+ //
+
+ if (!i) {
+ status = Serenum_Wait(&timer, DefaultWait);
+ if (!NT_SUCCESS(status)) {
+ Serenum_KdPrint (PFdoData, SER_DBG_SS_ERROR,
+ ("Timer failed with status %x\n", status ));
+ LOGENTRY(LOG_ENUM, 'SDED', PFdoData, status, 0);
+ break;
+ }
+ }
+
+ //
+ // set RTS
+ //
+
+ Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE, ("Setting RTS...\n"));
+
+ status = Serenum_IoSyncIoctl(IOCTL_SERIAL_SET_RTS, FALSE, pDevStack,
+ &event);
+
+ if (!NT_SUCCESS(status)) {
+ LOGENTRY(LOG_ENUM, 'SDEF', PFdoData, status, 0);
+ break;
+ }
+
+ //
+ // Read from the serial port
+ //
+ Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE,
+ ("Reading the serial port...\n"));
+
+ Serenum_KdPrint(PFdoData, SER_DBG_SS_INFO, ("Address: %p\n", pReadBuf));
+
+ nRead = 0;
+
+#if DBG
+ RtlFillMemory(pReadBuf, MAX_DEVNODE_NAME, 0xff);
+#endif
+
+ //
+ // Flush the input buffer
+ //
+
+ status = Serenum_ReadSerialPort((PCHAR)pReadBuf, MAX_DEVNODE_NAME,
+ SERENUM_SERIAL_READ_TIME, &nRead,
+ &ioStatusBlock, PFdoData);
+
+ switch (status) {
+ case STATUS_TIMEOUT:
+ if (nRead == 0) {
+ Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE,
+ ("Timeout with no bytes read; continuing\n"));
+ LOGENTRY(LOG_ENUM, 'SDEG', PFdoData, status, 0);
+ continue;
+ }
+
+ //
+ // We timed out with data, so we use what we have
+ //
+
+ status = STATUS_SUCCESS;
+
+ LOGENTRY(LOG_ENUM, 'SDEH', PFdoData, status, 0);
+ break;
+
+ case STATUS_SUCCESS:
+ Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE, ("Read succeeded\n"));
+ LOGENTRY(LOG_ENUM, 'SDEJ', PFdoData, status, 0);
+ goto ProtocolDone;
+ break;
+
+ default:
+ Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE, ("Read failed with 0x%x\n",
+ status));
+ LOGENTRY(LOG_ENUM, 'SDEK', PFdoData, status, 0);
+ goto ProtocolDone;
+ break;
+ }
+
+ //
+ // If anything was read from the serial port, we're done!
+ //
+
+ if (nRead) {
+ break;
+ }
+ }
+
+ProtocolDone:;
+
+ if (!NT_SUCCESS(status)) {
+ if (pReadBuf != NULL) {
+ ExFreePoolWithTag(pReadBuf,SERENUM_POOL_TAG);
+ pReadBuf = NULL;
+ return status;
+ }
+ }
+
+ *PNBytes = nRead;
+ *PpBuf = pReadBuf;
+ LOGENTRY(LOG_ENUM, 'SDE0', PFdoData, status, nRead);
+#pragma warning(suppress: 26045)
+ return status;
+}
+
+BOOLEAN
+SerenumValidateID(IN PUNICODE_STRING PId)
+/*++
+
+Routine Description:
+
+ This validates all the characters in a MULTI_SZ for a Pnp ID.
+
+ Invalid characters are:
+ c < 0x20 (' ')
+ c > 0x7F
+ c == 0x2C (',')
+
+Arguments:
+
+ PId - Pointer to a multi_sz containing the IDs
+
+Return value:
+
+ BOOLEAN -- TRUE if valid ID, FALSE otherwise
+
+--*/
+{
+ WCHAR *cp;
+
+ PAGED_CODE();
+
+ //
+ // Walk each string in the multisz and check for bad characters
+ //
+
+ cp = PId->Buffer;
+
+ if (cp == NULL) {
+ return TRUE;
+ }
+
+ do {
+ while (*cp) {
+ if ((*cp < L' ') || (*cp > L'\x7f') || (*cp == L',') ) {
+ return FALSE;
+ }
+
+ cp++;
+ }
+
+ cp++;
+ } while (*cp);
+
+ return TRUE;
+}
+
+BOOLEAN
+SerenumCheckForLegacyDevice(IN PFDO_DEVICE_DATA PFdoData,
+ _In_reads_(BufferLen) IN PCHAR PIdBuf,
+ IN ULONG BufferLen,
+ IN OUT PUNICODE_STRING PHardwareIDs,
+ IN OUT PUNICODE_STRING PCompIDs,
+ IN OUT PUNICODE_STRING PDeviceIDs)
+/*++
+
+Routine Description:
+
+ This routine implements legacy mouse detection
+
+Arguments:
+
+ PFdoData - pointer to the FDO's device-specific data
+ PIdBuf - Buffer of data returned from device
+ BufferLen - length of PIdBuf in bytes
+ PHardwareIDs - MULTI_SZ to return hardware ID's in
+ PCompIDs - MULTI_SZ to return compatible ID's in
+ PDeviceIDs - MULTI_SZ to return device ID's in
+
+Return value:
+
+ BOOLEAN -- TRUE if mouse detected, FALSE otherwise
+
+--*/
+{
+ PCHAR mouseId = PIdBuf;
+ ULONG mouseIdLen;
+ BOOLEAN rval = FALSE;
+
+ PAGED_CODE();
+
+ SerenumScanOtherIdForMouse(PIdBuf, BufferLen, &mouseId, &mouseIdLen);
+
+ if (mouseId != NULL) {
+ //
+ // A legacy device is attached to the serial port, since DSR was
+ // not set when RTS was set.
+ // If we find a mouse from the PIdBuf, copy the appropriate
+ // strings into the hardwareIDs and compIDs manually.
+ //
+ if (*mouseId == 'M') {
+ if ((mouseId - PIdBuf) > 1
+ && mouseIdLen > 1
+ && mouseId[1] == '3' ) {
+ Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE, ("*PNP0F08 mouse\n"));
+ Serenum_InitMultiString(PFdoData, PHardwareIDs, "*PNP0F08", NULL);
+ Serenum_InitMultiString(PFdoData, PCompIDs, "SERIAL_MOUSE", NULL);
+ //
+ // CIMEXCIMEX 04/28/1999 -
+ // Device ID's should be unique, at least as unique as the
+ // hardware ID's. This ID should really be Serenum\\PNP0F08
+ //
+ Serenum_InitMultiString(PFdoData, PDeviceIDs, "Serenum\\Mouse",
+ NULL);
+ rval = TRUE;
+
+ } else {
+ Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE, ("*PNP0F01 mouse\n"));
+ Serenum_InitMultiString(PFdoData, PHardwareIDs, "*PNP0F01", NULL);
+ Serenum_InitMultiString(PFdoData, PCompIDs, "SERIAL_MOUSE", NULL);
+ //
+ // CIMEXCIMEX 04/28/1999 -
+ // Device ID's should be unique, at least as unique as the
+ // hardware ID's. This ID should really be Serenum\\PNP0F01
+ //
+ Serenum_InitMultiString(PFdoData, PDeviceIDs, "Serenum\\Mouse",
+ NULL);
+ rval = TRUE;
+ }
+ } else if (*mouseId == 'B') {
+ Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE, ("*PNP0F09 mouse\n"));
+ Serenum_InitMultiString(PFdoData, PHardwareIDs, "*PNP0F09", NULL);
+ Serenum_InitMultiString(PFdoData, PCompIDs, "*PNP0F0F", "SERIAL_MOUSE",
+ NULL);
+ //
+ // CIMEXCIMEX 04/28/1999 -
+ // Device ID's should be unique, at least as unique as the
+ // hardware ID's. This ID should really be Serenum\\PNP0F09
+ //
+ Serenum_InitMultiString(PFdoData, PDeviceIDs, "Serenum\\BallPoint",
+ NULL);
+ rval = TRUE;
+ }
+
+#if DBG
+ if (rval) {
+ Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE,
+ ("Buffers at 0x%p 0x%p 0x%p\n",
+ PHardwareIDs->Buffer, PCompIDs->Buffer,
+ PDeviceIDs->Buffer));
+ }
+#endif // DBG
+
+ }
+
+ return rval;
+}
+
+NTSTATUS
+Serenum_ReenumerateDevices(IN PIRP Irp, IN PFDO_DEVICE_DATA PFdoData,
+ PBOOLEAN PSameDevice)
+/*++
+
+Routine Description:
+
+ This enumerates the serenum bus which is represented by Fdo (a pointer
+ to the device object representing the serial bus). It creates new PDOs
+ for any new devices which have been discovered since the last enumeration
+
+Arguments:
+
+ PFdoData - Pointer to the fdo's device extension
+ for the serial bus which needs to be enumerated
+ Irp - Pointer to the Irp which was sent to reenumerate.
+
+Return value:
+
+ NTSTATUS
+
+--*/
+{
+ NTSTATUS status;
+ KEVENT event;
+ KTIMER timer;
+
+ UNICODE_STRING pdoUniName;
+ PDEVICE_OBJECT pdo = PFdoData->NewPDO;
+ PDEVICE_OBJECT pDevStack = PFdoData->TopOfStack;
+ PPDO_DEVICE_DATA pdoData;
+ UNICODE_STRING hardwareIDs;
+ UNICODE_STRING compIDs;
+ UNICODE_STRING deviceIDs;
+ UNICODE_STRING devDesc;
+ UNICODE_STRING serNo;
+ UNICODE_STRING pnpRev;
+
+
+ BOOLEAN DSRMissing = FALSE;
+ BOOLEAN legacyDeviceFound = FALSE;
+
+ USHORT nActual = 0;
+
+ PCHAR pReadBuf = NULL;
+ WCHAR pdoName[] = SERENUM_PDO_NAME_BASE;
+
+ SERIAL_BASIC_SETTINGS basicSettings;
+ BOOLEAN basicSettingsDone = FALSE;
+ SERIAL_TIMEOUTS timeouts, newTimeouts;
+
+ ULONG curTry = 0;
+ BOOLEAN sameDevice = FALSE;
+
+ PAGED_CODE();
+
+ //
+ // While enumeration is taking place, we can't allow a Create to come down
+ // from an upper driver. We use this semaphore to protect ourselves.
+ //
+
+ status = KeWaitForSingleObject(&PFdoData->CreateSemaphore, Executive,
+ KernelMode, FALSE, NULL);
+
+ if (!NT_SUCCESS(status)) {
+ return status;
+ }
+
+
+ //
+ // Initialization
+ //
+
+ RtlInitUnicodeString(&pdoUniName, pdoName);
+ pdoName[((sizeof(pdoName)/sizeof(WCHAR)) - 2)] = L'0' + PFdoData->PdoIndex++;
+
+ KeInitializeEvent(&event, NotificationEvent, FALSE);
+ KeInitializeTimer(&timer);
+
+ RtlInitUnicodeString(&hardwareIDs, NULL);
+ RtlInitUnicodeString(&compIDs, NULL);
+ RtlInitUnicodeString(&deviceIDs, NULL);
+ RtlInitUnicodeString(&devDesc, NULL);
+ RtlInitUnicodeString(&serNo, NULL);
+ RtlInitUnicodeString(&pnpRev, NULL);
+
+ //
+ // If the current PDO should be marked missing, do so.
+ //
+ if (PFdoData->PDOForcedRemove && pdo != NULL) {
+ Serenum_PDO_EnumMarkMissing(PFdoData, pdo->DeviceExtension);
+ pdo = NULL;
+ }
+
+ //
+ // Open the Serial port before sending Irps down
+ // Use the Irp passed to us, and grab it on the way up.
+ //
+
+ Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE,
+ ("Opening the serial port...\n"));
+
+ status = Serenum_IoSyncReqWithIrp(Irp, IRP_MJ_CREATE, &event, pDevStack);
+
+ LOGENTRY(LOG_ENUM, 'SRRO', PFdoData, status, 0);
+
+ //
+ // If we cannot open the stack, odd's are we have a live and started PDO on
+ // it. Since enumeration might interfere with running devices, we do not
+ // adjust our list of children if we cannot open the stack.
+ //
+ if (!NT_SUCCESS(status)) {
+ Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE,
+ ("Failed to open the serial port...\n"));
+ KeReleaseSemaphore(&PFdoData->CreateSemaphore, IO_NO_INCREMENT, 1, FALSE);
+ LOGENTRY(LOG_ENUM, 'SRR1', PFdoData, status, 0);
+ return status;
+ }
+
+ //
+ // Set up the COM port
+ //
+
+ Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE, ("Setting up port\n"));
+
+ status = Serenum_IoSyncIoctlEx(IOCTL_SERIAL_INTERNAL_BASIC_SETTINGS, TRUE,
+ pDevStack, &event, NULL, 0,
+ &basicSettings, sizeof(basicSettings));
+
+ if (NT_SUCCESS(status)) {
+ basicSettingsDone = TRUE;
+ } else {
+ //
+ // This "serial" driver doesn't support BASIC_SETTINGS so instead
+ // we just set what we really need the old fashioned way
+ //
+
+ status = Serenum_IoSyncIoctlEx(IOCTL_SERIAL_GET_TIMEOUTS, FALSE,
+ pDevStack, &event,
+ NULL, 0, &timeouts, sizeof(timeouts));
+
+ if (!NT_SUCCESS(status)) {
+ //
+ // This should not happen because we are sending an Ioctl to Serial
+ // but for robustness of the code we check the return status.
+ //
+ Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE,
+ ("Failed to get the serial timeouts...\n"));
+ KeReleaseSemaphore(&PFdoData->CreateSemaphore, IO_NO_INCREMENT, 1, FALSE);
+ return status;
+ }
+
+ RtlZeroMemory(&newTimeouts, sizeof(newTimeouts));
+
+ Serenum_IoSyncIoctlEx(IOCTL_SERIAL_SET_TIMEOUTS, FALSE, pDevStack, &event,
+ &newTimeouts, sizeof(newTimeouts), NULL, 0);
+ }
+
+
+ //
+ // Run the serial PnP device detection protocol; give it up to 3 tries
+ //
+
+ while (curTry <= 2) {
+ if (pReadBuf) {
+ ExFreePoolWithTag(pReadBuf,SERENUM_POOL_TAG);
+ pReadBuf = NULL;
+ }
+
+ status = SerenumDoEnumProtocol(PFdoData, (PUCHAR*)&pReadBuf, &nActual,
+ &DSRMissing);
+
+ if (status == STATUS_SUCCESS) {
+ break;
+ }
+
+ curTry++;
+ }
+
+ //
+ // If DSR wasn't set any existing pdos will be eliminated
+ //
+
+
+ if (basicSettingsDone) {
+ Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE,
+ ("Restoring basic settings\n"));
+
+ Serenum_IoSyncIoctlEx(IOCTL_SERIAL_INTERNAL_RESTORE_SETTINGS, TRUE,
+ pDevStack, &event, &basicSettings,
+ sizeof(basicSettings), NULL, 0);
+ } else {
+ Serenum_IoSyncIoctlEx(IOCTL_SERIAL_SET_TIMEOUTS, FALSE, pDevStack, &event,
+ &timeouts, sizeof(timeouts), NULL, 0);
+ }
+
+ //
+ // Cleanup and then Close
+ //
+
+ Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE,
+ ("Cleanup on the serial port...\n"));
+
+ //
+ // We ignore the status -- we have to finish closing
+ //
+
+ (void)Serenum_IoSyncReqWithIrp(Irp, IRP_MJ_CLEANUP, &event, pDevStack);
+
+#if DBG
+ if (!NT_SUCCESS(status)) {
+ Serenum_KdPrint(PFdoData, SER_DBG_SS_ERROR,
+ ("Failed to cleanup the serial port...\n"));
+ // don't return because we want to attempt to close!
+ }
+#endif
+
+ //
+ // Close the Serial port after everything is done
+ //
+
+ Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE,
+ ("Closing the serial port...\n"));
+
+ //
+ // We ignore the status -- we have to close!!
+ //
+
+ Serenum_IoSyncReqWithIrp(Irp, IRP_MJ_CLOSE, &event, pDevStack);
+
+ LOGENTRY(LOG_ENUM, 'SRRC', PFdoData, 0, 0);
+
+ //
+ // Our status is that of the enumeration
+ //
+
+ if (!NT_SUCCESS(status)) {
+ Serenum_KdPrint(PFdoData, SER_DBG_SS_ERROR,
+ ("Failed to enumerate the serial port...\n"));
+ KeReleaseSemaphore(&PFdoData->CreateSemaphore, IO_NO_INCREMENT, 1, FALSE);
+ if (pReadBuf != NULL) {
+ ExFreePoolWithTag(pReadBuf,SERENUM_POOL_TAG);
+ }
+ LOGENTRY(LOG_ENUM, 'SRR2', PFdoData, status, 0);
+ return status;
+ }
+
+ //
+ // Check if anything was read, and if not, we're done
+ //
+
+ if (nActual == 0) {
+ if (pReadBuf != NULL) {
+ ExFreePoolWithTag(pReadBuf,SERENUM_POOL_TAG);
+ pReadBuf = NULL;
+ }
+
+ if (pdo != NULL) {
+ //
+ // Something was there. The device must have been unplugged.
+ // Remove the PDO.
+ //
+
+ Serenum_PDO_EnumMarkMissing(PFdoData, pdo->DeviceExtension);
+ pdo = NULL;
+ }
+
+ goto ExitReenumerate;
+ }
+
+ Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE,
+ ("Something was read from the serial port...\n"));
+
+
+
+#if 0
+ if (PFdoData->DebugLevel & SER_DBG_PNP_DUMP_PACKET) {
+ SerenumHexDump(pReadBuf, nActual);
+ }
+#endif
+
+ //
+ // Determine from the result whether the current pdo (if we have one),
+ // should be deleted. If it's the same device, then keep it. If it's a
+ // different device or if the device is a legacy device, then create a
+ // new pdo.
+ //
+ if (DSRMissing) {
+ legacyDeviceFound
+ = SerenumCheckForLegacyDevice(PFdoData, pReadBuf, nActual,
+ &hardwareIDs, &compIDs, &deviceIDs);
+ }
+
+ if (!legacyDeviceFound) {
+ //
+ // No legacy device was found, so parse the data we got back
+ // from the device.
+ //
+
+ status = Serenum_ParseData(PFdoData, pReadBuf, nActual, &hardwareIDs,
+ &compIDs, &deviceIDs, &devDesc, &serNo, &pnpRev);
+
+ //
+ // Last chance:
+ //
+ // 1) DSR is present
+ // 2) Not a PnP device
+ //
+ // There are some devices that are legacy but also assert DSR (e.g., the
+ // gyropoint mouse). Give it one last shot.
+ //
+
+ if (!DSRMissing && !NT_SUCCESS(status)) {
+
+
+ //
+ // CIMEXCIMEX Serenum_ParseData() isn't very tidy, so we
+ // must clean up after them
+ //
+
+ SerenumFreeUnicodeString(&hardwareIDs);
+ SerenumFreeUnicodeString(&compIDs);
+ SerenumFreeUnicodeString(&deviceIDs);
+ SerenumFreeUnicodeString(&devDesc);
+ SerenumFreeUnicodeString(&serNo);
+ SerenumFreeUnicodeString(&pnpRev);
+
+ if (SerenumCheckForLegacyDevice(PFdoData, pReadBuf, nActual,
+ &hardwareIDs, &compIDs, &deviceIDs)) {
+ status = STATUS_SUCCESS;
+ }
+ }
+
+ //
+ // If the data can't be parsed and this isn't a legacy device, then
+ // it is something we don't understand. We bail out at this point
+ //
+
+
+ if (!NT_SUCCESS(status)) {
+ Serenum_KdPrint(PFdoData, SER_DBG_SS_ERROR,
+ ("Failed to parse the data for the new device\n"));
+
+ //
+ // If there is a current PDO, remove it since we can't ID the
+ // attached device.
+ //
+
+ if (pdo) {
+ Serenum_PDO_EnumMarkMissing(PFdoData, pdo->DeviceExtension);
+ pdo = NULL;
+ }
+
+ SerenumFreeUnicodeString(&hardwareIDs);
+ SerenumFreeUnicodeString(&compIDs);
+ SerenumFreeUnicodeString(&deviceIDs);
+ SerenumFreeUnicodeString(&devDesc);
+ SerenumFreeUnicodeString(&serNo);
+ SerenumFreeUnicodeString(&pnpRev);
+
+
+ ExFreePoolWithTag(pReadBuf,SERENUM_POOL_TAG);
+ pReadBuf = NULL;
+
+ goto ExitReenumerate;
+ }
+ }
+
+ //
+ // We're now finally able to free this read buffer.
+ //
+
+ if (pReadBuf != NULL) {
+ ExFreePoolWithTag(pReadBuf,SERENUM_POOL_TAG);
+ }
+
+ //
+ // Validate all the ID's -- if any are illegal,
+ // then we fail the enumeration
+ //
+
+ if (!SerenumValidateID(&hardwareIDs) || !SerenumValidateID(&compIDs)
+ || !SerenumValidateID(&deviceIDs)) {
+
+ //
+ // If a PDO already exists, mark it missing and get rid
+ // of it since we don't know what is out there any longer
+ //
+
+ if (pdo) {
+ Serenum_PDO_EnumMarkMissing(PFdoData, pdo->DeviceExtension);
+ pdo = NULL;
+ }
+
+ SerenumFreeUnicodeString(&hardwareIDs);
+ SerenumFreeUnicodeString(&compIDs);
+ SerenumFreeUnicodeString(&deviceIDs);
+ SerenumFreeUnicodeString(&devDesc);
+ SerenumFreeUnicodeString(&serNo);
+ SerenumFreeUnicodeString(&pnpRev);
+
+
+ goto ExitReenumerate;
+ }
+
+ //
+ // Check if the current device is the same as the one that we're
+ // enumerating. If so, we'll just keep the current pdo.
+ //
+ if (pdo) {
+ pdoData = pdo->DeviceExtension;
+
+ //
+ // CIMEXCIMEX 04/28/1999 -
+ // We should be comparing device ID's here, but the above mentioned
+ // bug must be fixed first. Note that even this code is broken as it
+ // doesn't take into account that hardware/compID's are multiSz.
+ //
+
+ if (!(RtlEqualUnicodeString(&pdoData->HardwareIDs, &hardwareIDs, FALSE)
+ && RtlEqualUnicodeString(&pdoData->CompIDs, &compIDs, FALSE))) {
+ //
+ // The ids are not the same, so get rid of this pdo and create a
+ // new one so that the PNP system will query the ids and find a
+ // new driver
+ //
+ Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE, ("Different device."
+ " Removing PDO %p\n",
+ pdo));
+ Serenum_PDO_EnumMarkMissing(PFdoData, pdoData);
+ pdo = NULL;
+ } else {
+ Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE,
+ ("Same device. Keeping current Pdo %p\n", pdo));
+ sameDevice = TRUE;
+ }
+ }
+
+ //
+ // If there isn't a pdo, then create one!
+ //
+ if (!pdo) {
+ //
+ // Allocate a pdo
+ //
+ status = IoCreateDevice(PFdoData->Self->DriverObject,
+ sizeof(PDO_DEVICE_DATA), &pdoUniName,
+ FILE_DEVICE_UNKNOWN,
+ FILE_AUTOGENERATED_DEVICE_NAME, FALSE, &pdo);
+
+ if (!NT_SUCCESS(status)) {
+
+ Serenum_KdPrint(PFdoData, SER_DBG_SS_ERROR,
+ ("Create device failed\n"));
+ KeReleaseSemaphore(&PFdoData->CreateSemaphore, IO_NO_INCREMENT, 1,
+ FALSE);
+ return status;
+ }
+
+ Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE,
+ ("Created PDO on top of filter: %p\n",pdo));
+
+
+ //
+ // Initialize the rest of the device object
+ //
+
+
+ pdoData = pdo->DeviceExtension;
+
+ //
+ // Copy our temp buffers over to the DevExt
+ //
+
+ pdoData->HardwareIDs = hardwareIDs;
+ pdoData->CompIDs = compIDs;
+ pdoData->DeviceIDs = deviceIDs;
+ pdoData->DevDesc = devDesc;
+ pdoData->SerialNo = serNo;
+ pdoData->PnPRev = pnpRev;
+
+ Serenum_InitPDO(pdo, PFdoData);
+
+ }
+
+ExitReenumerate:;
+
+ KeReleaseSemaphore(&PFdoData->CreateSemaphore, IO_NO_INCREMENT, 1, FALSE);
+
+ *PSameDevice = sameDevice;
+
+ return STATUS_SUCCESS;
+}
+
+void
+Serenum_PDO_EnumMarkMissing(PFDO_DEVICE_DATA FdoData, PPDO_DEVICE_DATA PdoData)
+/*++
+
+Routine Description:
+ Removes the attached pdo from the fdo's list of children.
+
+ NOTE: THIS FUNCTION CAN ONLY BE CALLED DURING AN ENUMERATION. If called
+ outside of enumeration, Serenum might delete it's PDO before PnP has
+ been told the PDO is gone.
+
+Arguments:
+ FdoData - Pointer to the fdo's device extension
+ PdoData - Pointer to the pdo's device extension
+
+Return value:
+ none
+
+--*/
+{
+ KIRQL oldIrql;
+
+ Serenum_KdPrint (FdoData, SER_DBG_SS_TRACE, ("Removing Pdo %p\n",
+ PdoData->Self));
+
+ ASSERT(PdoData->Attached);
+
+ KeAcquireSpinLock(&FdoData->EnumerationLock, &oldIrql);
+
+ PdoData->Attached = FALSE;
+ FdoData->NewPDO = NULL;
+ FdoData->NewPdoData = NULL;
+ FdoData->NewNumPDOs = 0;
+ FdoData->NewPDOForcedRemove = FALSE;
+
+ FdoData->EnumFlags |= SERENUM_ENUMFLAG_DIRTY;
+
+ KeReleaseSpinLock(&FdoData->EnumerationLock, oldIrql);
+}
+
+NTSTATUS
+Serenum_IoSyncReqWithIrp(PIRP PIrp, UCHAR MajorFunction, PKEVENT PEvent,
+ PDEVICE_OBJECT PDevObj )
+/*++
+
+Routine Description:
+ Performs a synchronous IO request by waiting on the event object
+ passed to it. The IRP isn't deallocated after this call.
+
+Arguments:
+ PIrp - The IRP to be used for this request
+
+ MajorFunction - The major function
+
+ PEvent - An event used to wait for the IRP
+
+ PDevObj - The object that we're performing the IO request upon
+
+Return value:
+ NTSTATUS
+
+--*/
+{
+ PIO_STACK_LOCATION stack;
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ stack = IoGetNextIrpStackLocation(PIrp);
+
+ stack->MajorFunction = MajorFunction;
+
+ KeClearEvent(PEvent);
+
+ IoSetCompletionRoutine(PIrp, Serenum_EnumComplete, PEvent, TRUE,
+ TRUE, TRUE);
+
+ status = Serenum_IoSyncReq(PDevObj, PIrp, PEvent);
+
+ if (status == STATUS_SUCCESS) {
+ status = PIrp->IoStatus.Status;
+ }
+
+ return status;
+}
+
+NTSTATUS
+Serenum_IoSyncIoctlEx(ULONG Ioctl, BOOLEAN Internal, PDEVICE_OBJECT PDevObj,
+ PKEVENT PEvent, PVOID PInBuffer, ULONG InBufferLen,
+ PVOID POutBuffer, ULONG OutBufferLen)
+/*++
+
+Routine Description:
+ Performs a synchronous IO control request by waiting on the event object
+ passed to it. The IRP is deallocated by the IO system when finished.
+
+Return value:
+ NTSTATUS
+
+--*/
+{
+ PIRP pIrp;
+ NTSTATUS status;
+ IO_STATUS_BLOCK IoStatusBlock;
+
+ PAGED_CODE();
+
+ KeClearEvent(PEvent);
+
+ // Allocate an IRP - No need to release
+ // When the next-lower driver completes this IRP, the IO Mgr releases it.
+
+ pIrp = IoBuildDeviceIoControlRequest(Ioctl, PDevObj, PInBuffer, InBufferLen,
+ POutBuffer, OutBufferLen, Internal,
+ PEvent, &IoStatusBlock);
+
+ if (pIrp == NULL) {
+ Serenum_KdPrint_Def (SER_DBG_SS_ERROR, ("Failed to allocate IRP\n"));
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ status = Serenum_IoSyncReq(PDevObj, pIrp, PEvent);
+
+
+ if (status == STATUS_SUCCESS) {
+ status = IoStatusBlock.Status;
+ }
+
+ return status;
+}
+
+
+NTSTATUS
+Serenum_IoSyncReq(PDEVICE_OBJECT PDevObj, IN PIRP PIrp, PKEVENT PEvent)
+/*++
+
+Routine Description:
+ Performs a synchronous IO request by waiting on the event object
+ passed to it. The IRP is deallocated by the IO system when finished.
+
+Return value:
+ NTSTATUS
+
+--*/
+{
+ NTSTATUS status;
+ PLARGE_INTEGER Timeout=NULL;
+
+ PAGED_CODE();
+
+ status = IoCallDriver(PDevObj, PIrp);
+
+ if (status == STATUS_PENDING) {
+ // wait for it...
+ __analysis_assume(Timeout!=NULL);
+ status = KeWaitForSingleObject(PEvent, Executive, KernelMode, FALSE,
+ Timeout);
+ }
+
+ return status;
+}
+
+NTSTATUS
+Serenum_Wait(IN PKTIMER Timer, IN LARGE_INTEGER DueTime)
+/*++
+
+Routine Description:
+ Performs a wait for the specified time.
+ NB: Negative time is relative to the current time. Positive time
+ represents an absolute time to wait until.
+
+Return value:
+ NTSTATUS
+
+--*/
+{
+ PAGED_CODE();
+
+ if (KeSetTimer(Timer, DueTime, NULL)) {
+ Serenum_KdPrint_Def(SER_DBG_SS_INFO, ("Timer already set: %p\n", Timer));
+ }
+
+ return KeWaitForSingleObject(Timer, Executive, KernelMode, FALSE, NULL);
+}
+
+
+NTSTATUS
+Serenum_CompletionRoutine(
+ PDEVICE_OBJECT DeviceObject,
+ PIRP Irp,
+ PVOID Context
+ )
+/*++
+
+Routine Description:
+
+ The completion routine for plug & play irps that needs to be
+ processed first by the lower drivers.
+
+Arguments:
+
+ DeviceObject - pointer to a device object.
+
+ Irp - pointer to an I/O Request Packet.
+
+ Context - pointer to an event object.
+
+Return Value:
+
+ NT status code
+
+--*/
+{
+ PCOMMON_DEVICE_DATA commonData=(PCOMMON_DEVICE_DATA)DeviceObject->DeviceExtension;
+ UNREFERENCED_PARAMETER(Context);
+
+ // the driver must mark the irp as pending if PendingReturned is set.
+ if (Irp->PendingReturned == TRUE){
+ IoMarkIrpPending(Irp);
+ }
+ // release the removelock
+ IoReleaseRemoveLock(&commonData->RemoveLock, Irp);
+ return STATUS_CONTINUE_COMPLETION;
+
+}
+
+
+
+
+NTSTATUS
+Serenum_EnumComplete (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PVOID Context
+ )
+/*++
+Routine Description:
+ A completion routine for use when calling the lower device objects to
+ which our bus (FDO) is attached. It sets the event for the synchronous
+ calls done.
+
+--*/
+{
+ UNREFERENCED_PARAMETER(DeviceObject);
+ UNREFERENCED_PARAMETER(Irp);
+
+#pragma warning(suppress: 6387)
+ KeSetEvent((PKEVENT)Context, IO_NO_INCREMENT, FALSE);
+ // No special priority
+ // No Wait
+
+ return STATUS_MORE_PROCESSING_REQUIRED; // Keep this IRP
+}
+
+
+NTSTATUS
+Serenum_ReadSerialPort(
+ _Out_writes_(Buflen) OUT PCHAR PReadBuffer, IN USHORT Buflen,
+ IN ULONG Timeout, OUT PUSHORT nActual,
+ OUT PIO_STATUS_BLOCK PIoStatusBlock,
+ IN const PFDO_DEVICE_DATA FdoData)
+{
+ NTSTATUS status;
+ PIRP pIrp;
+ LARGE_INTEGER startingOffset;
+ KEVENT event;
+ SERIAL_TIMEOUTS timeouts;
+
+ PAGED_CODE();
+
+ startingOffset.QuadPart = (LONGLONG) 0;
+ //
+ // Set the proper timeouts for the read
+ //
+
+ timeouts.ReadIntervalTimeout = MAXULONG;
+ timeouts.ReadTotalTimeoutMultiplier = MAXULONG;
+ timeouts.ReadTotalTimeoutConstant = Timeout;
+ timeouts.WriteTotalTimeoutMultiplier = 0;
+ timeouts.WriteTotalTimeoutConstant = 0;
+
+ KeInitializeEvent(&event, NotificationEvent, FALSE);
+
+ status = Serenum_IoSyncIoctlEx(IOCTL_SERIAL_SET_TIMEOUTS, FALSE,
+ FdoData->TopOfStack, &event, &timeouts,
+ sizeof(timeouts), NULL, 0);
+
+ if (!NT_SUCCESS(status)) {
+ return status;
+ }
+
+ Serenum_KdPrint(FdoData, SER_DBG_SS_TRACE, ("Read pending...\n"));
+
+ *nActual = 0;
+
+ while (*nActual < Buflen) {
+ KeClearEvent(&event);
+
+ pIrp = IoBuildSynchronousFsdRequest(IRP_MJ_READ, FdoData->TopOfStack,
+ PReadBuffer, 1, &startingOffset,
+ &event, PIoStatusBlock);
+
+ if (pIrp == NULL) {
+ Serenum_KdPrint(FdoData, SER_DBG_SS_ERROR, ("Failed to allocate IRP"
+ "\n"));
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ status = IoCallDriver(FdoData->TopOfStack, pIrp);
+
+ if (status == STATUS_PENDING) {
+
+ //
+ // Wait for the IRP
+ //
+
+ status = KeWaitForSingleObject(&event, Executive, KernelMode,
+ FALSE, NULL);
+
+ if (status == STATUS_SUCCESS) {
+ status = PIoStatusBlock->Status;
+ }
+ }
+
+ if (!NT_SUCCESS(status) || status == STATUS_TIMEOUT) {
+ Serenum_KdPrint (FdoData, SER_DBG_SS_ERROR,
+ ("IO Call failed with status %x\n", status));
+ return status;
+ }
+
+ *nActual += (USHORT)PIoStatusBlock->Information;
+ PReadBuffer += (USHORT)PIoStatusBlock->Information;
+ }
+
+ return status;
+}
+
+
+NTSTATUS
+Serenum_GetRegistryKeyValue(
+ _In_ HANDLE Handle,
+ _In_reads_bytes_(KeyNameStringLength) PWCHAR KeyNameString,
+ _In_ ULONG KeyNameStringLength,
+ _Out_writes_bytes_to_(DataLength, *ActualLength) PVOID Data,
+ _In_ ULONG DataLength,
+ _Out_ PULONG ActualLength
+ )
+/*++
+
+Routine Description:
+
+ Reads a registry key value from an already opened registry key.
+
+Arguments:
+
+ Handle Handle to the opened registry key
+
+ KeyNameString ANSI string to the desired key
+
+ KeyNameStringLength Length of the KeyNameString
+
+ Data Buffer to place the key value in
+
+ DataLength Length of the data buffer
+
+Return Value:
+
+ STATUS_SUCCESS if all works, otherwise status of system call that
+ went wrong.
+
+--*/
+{
+ UNICODE_STRING keyName;
+ ULONG length;
+ PKEY_VALUE_FULL_INFORMATION fullInfo = NULL;
+
+ NTSTATUS ntStatus = STATUS_INSUFFICIENT_RESOURCES;
+
+#pragma warning(suppress: 26035)
+#pragma warning(suppress: 26018)
+ RtlInitUnicodeString (&keyName, KeyNameString);
+
+ length = sizeof(KEY_VALUE_FULL_INFORMATION) + KeyNameStringLength
+ + DataLength;
+ fullInfo = ExAllocatePoolWithTag(PagedPool, length,SERENUM_POOL_TAG);
+
+ if (ActualLength != NULL) {
+ *ActualLength = 0;
+ }
+
+ if (fullInfo) {
+#pragma warning(suppress: 26018)
+ ntStatus = ZwQueryValueKey(Handle, &keyName, KeyValueFullInformation,
+ fullInfo, length, &length);
+
+ if (NT_SUCCESS(ntStatus)) {
+ //
+ // If there is enough room in the data buffer, copy the output
+ //
+
+ if (DataLength >= fullInfo->DataLength) {
+ RtlCopyMemory(Data, ((PUCHAR)fullInfo) + fullInfo->DataOffset,
+ fullInfo->DataLength);
+ if (ActualLength != NULL) {
+ *ActualLength = fullInfo->DataLength;
+ }
+ }
+ }
+
+ ExFreePoolWithTag(fullInfo,SERENUM_POOL_TAG);
+ fullInfo = NULL;
+ }
+
+ if (!NT_SUCCESS(ntStatus) && !NT_ERROR(ntStatus)) {
+ if (ntStatus == STATUS_BUFFER_OVERFLOW) {
+ ntStatus = STATUS_BUFFER_TOO_SMALL;
+ } else {
+ ntStatus = STATUS_UNSUCCESSFUL;
+ }
+ }
+#pragma warning(suppress: 26045)
+ return ntStatus;
+}
+
+
+VOID
+SerenumWaitForEnumThreadTerminate(IN PFDO_DEVICE_DATA PFdoData)
+{
+ KIRQL oldIrql;
+ PVOID pThreadObj;
+
+ //
+ // Take a reference under the lock so the thread can't disappear on us.
+ //
+
+ KeAcquireSpinLock(&PFdoData->EnumerationLock, &oldIrql);
+
+ //
+ // If the work item beat us, then the thread is done and we can
+ // delete/stop/unload. Otherwise, we have to wait. We can use
+ // the reference we stole to hold the object around.
+ //
+
+ if (PFdoData->ThreadObj != NULL) {
+ pThreadObj = PFdoData->ThreadObj;
+ PFdoData->ThreadObj = NULL;
+ PFdoData->EnumFlags &= ~SERENUM_ENUMFLAG_PENDING;
+ } else {
+ pThreadObj = NULL;
+ }
+
+ KeReleaseSpinLock(&PFdoData->EnumerationLock, oldIrql);
+
+ if (pThreadObj != NULL) {
+ KeWaitForSingleObject(pThreadObj, Executive, KernelMode, FALSE, NULL);
+ ObDereferenceObject(pThreadObj);
+ }
+}
+
+
+IO_WORKITEM_ROUTINE
+SerenumEnumThreadWorkItem;
+
+VOID
+SerenumEnumThreadWorkItem(IN PDEVICE_OBJECT PDevObj, IN PVOID PFdoData)
+{
+ PFDO_DEVICE_DATA pFdoData = (PFDO_DEVICE_DATA)PFdoData;
+ KIRQL oldIrql;
+ PVOID pThreadObj;
+ PIO_WORKITEM pWorkItem;
+
+ _Analysis_assume_(pFdoData != NULL); // // Not NULL when passed to IoQueueWorkItem()
+ UNREFERENCED_PARAMETER(PDevObj);
+
+ //
+ // See if the delete/stop code beat us to the thread obj.
+ // If not, we can derefence the thread.
+ //
+
+ KeAcquireSpinLock(&pFdoData->EnumerationLock, &oldIrql);
+
+ if (pFdoData->ThreadObj != NULL) {
+ pThreadObj = pFdoData->ThreadObj;
+ pFdoData->ThreadObj = NULL;
+ pFdoData->EnumFlags &= ~SERENUM_ENUMFLAG_PENDING;
+ } else {
+ pThreadObj = NULL;
+ }
+
+ pWorkItem = pFdoData->EnumWorkItem;
+ pFdoData->EnumWorkItem = NULL;
+
+ KeReleaseSpinLock(&pFdoData->EnumerationLock, oldIrql);
+
+ if (pThreadObj != NULL) {
+ ObDereferenceObject(pThreadObj);
+ }
+
+ IoFreeWorkItem(pWorkItem);
+}
+
+
+KSTART_ROUTINE
+SerenumEnumThread;
+
+VOID
+SerenumEnumThread(IN PVOID PFdoData)
+{
+ PFDO_DEVICE_DATA pFdoData = (PFDO_DEVICE_DATA)PFdoData;
+ PIRP pIrp = NULL;
+ NTSTATUS status;
+ KIRQL oldIrql;
+ PKTHREAD pThread;
+ BOOLEAN sameDevice = TRUE;
+
+ pThread = KeGetCurrentThread();
+
+ KeSetPriorityThread(pThread, HIGH_PRIORITY);
+
+ pIrp = IoAllocateIrp(pFdoData->TopOfStack->StackSize + 1, FALSE);
+
+ if (pIrp == NULL) {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ goto SerenumEnumThreadErrOut;
+ }
+ IoSetNextIrpStackLocation(pIrp);
+ status = Serenum_ReenumerateDevices(pIrp, pFdoData, &sameDevice);
+
+SerenumEnumThreadErrOut:
+
+ if (pIrp != NULL) {
+ IoFreeIrp(pIrp);
+ }
+
+ KeAcquireSpinLock(&pFdoData->EnumerationLock, &oldIrql);
+
+ if ((status == STATUS_SUCCESS) && !sameDevice) {
+ pFdoData->EnumFlags |= SERENUM_ENUMFLAG_DIRTY;
+ }
+
+ KeReleaseSpinLock(&pFdoData->EnumerationLock, oldIrql);
+
+ if ((status == STATUS_SUCCESS) && !sameDevice) {
+ IoInvalidateDeviceRelations(pFdoData->UnderlyingPDO, BusRelations);
+ }
+
+ //
+ // Queue a work item to release the last reference if remove/stop
+ // hasn't already.
+ //
+
+ IoQueueWorkItem(pFdoData->EnumWorkItem, SerenumEnumThreadWorkItem,
+ DelayedWorkQueue, pFdoData);
+
+ PsTerminateSystemThread(STATUS_SUCCESS);
+}
+
+
+NTSTATUS
+SerenumStartProtocolThread(IN PFDO_DEVICE_DATA PFdoData)
+{
+ NTSTATUS status;
+ OBJECT_ATTRIBUTES objAttrib;
+ HANDLE handle;
+ PVOID tmpObj;
+ KIRQL oldIrql;
+ PIO_WORKITEM pWorkItem;
+
+ InitializeObjectAttributes(&objAttrib, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
+
+ pWorkItem = IoAllocateWorkItem(PFdoData->Self);
+
+ if (pWorkItem == NULL) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ PFdoData->EnumWorkItem = pWorkItem;
+
+ status = PsCreateSystemThread(&handle, THREAD_ALL_ACCESS, NULL, NULL, NULL,
+ SerenumEnumThread, PFdoData);
+
+ if (NT_SUCCESS(status)) {
+
+ ASSERT(PFdoData->ThreadObj == NULL);
+
+ //
+ // We do this merely to get an object pointer that the remove
+ // code can wait on.
+ //
+
+ status = ObReferenceObjectByHandle(handle, THREAD_ALL_ACCESS, NULL,
+ KernelMode, &tmpObj, NULL);
+
+
+ KeAcquireSpinLock(&PFdoData->EnumerationLock, &oldIrql);
+
+ if (NT_SUCCESS(status)) {
+ PFdoData->ThreadObj = tmpObj;
+ KeReleaseSpinLock(&PFdoData->EnumerationLock, oldIrql);
+ } else {
+ //
+ // The thread may be done by now, so no one would need to
+ // synchronize with it.
+ //
+
+ PFdoData->ThreadObj = NULL;
+ PFdoData->EnumWorkItem = NULL;
+ KeReleaseSpinLock(&PFdoData->EnumerationLock, oldIrql);
+
+ }
+
+ //
+ // Close the handle so the only references possible are the ones
+ // for the thread itself and the one either the work item or
+ // remove will take care of
+ //
+
+ ZwClose(handle);
+ } else {
+ PFdoData->EnumWorkItem = NULL;
+ IoFreeWorkItem(pWorkItem);
+ }
+
+ return status;
+}
+
+
+#if _MSC_VER >= 1200
+#pragma warning(pop)
+#endif
+
+#pragma warning(default:4127) // conditional expression is constant
+
+
+
+
diff --git a/serial/serenum/log.c b/serial/serenum/log.c
new file mode 100644
index 00000000..693bb698
--- /dev/null
+++ b/serial/serenum/log.c
@@ -0,0 +1,185 @@
+/*++
+
+Copyright (c) 1995,1996 Microsoft Corporation
+:ts=4
+
+Module Name:
+
+ log.c
+
+Abstract:
+
+ Debug log Code for serial.
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+Revision History:
+
+ 10-08-95 : created
+
+--*/
+
+#include "pch.h"
+#include <stdio.h>
+
+#if DBG
+
+KSPIN_LOCK LogSpinLock;
+
+struct SERENUM_LOG_ENTRY {
+ ULONG le_sig; // Identifying string
+ ULONG_PTR le_info1; // entry specific info
+ ULONG_PTR le_info2; // entry specific info
+ ULONG_PTR le_info3; // entry specific info
+}; // SERENUM_LOG_ENTRY
+
+
+struct SERENUM_LOG_ENTRY *SerenumLStart = 0; // No log yet
+struct SERENUM_LOG_ENTRY *SerenumLPtr;
+struct SERENUM_LOG_ENTRY *SerenumLEnd;
+
+ULONG LogMask = 0xffffffff;
+
+VOID
+SerenumDebugLogEntry(IN ULONG Mask, IN ULONG Sig, IN ULONG_PTR Info1,
+ IN ULONG_PTR Info2, IN ULONG_PTR Info3)
+/*++
+
+Routine Description:
+
+ Adds an Entry to serial log.
+
+Arguments:
+
+Return Value:
+
+ None.
+
+--*/
+{
+ KIRQL irql;
+
+typedef union _SIG {
+ struct {
+ UCHAR Byte0;
+ UCHAR Byte1;
+ UCHAR Byte2;
+ UCHAR Byte3;
+ } b;
+ ULONG l;
+} SIG, *PSIG;
+
+ SIG sig, rsig;
+
+
+ if (SerenumLStart == 0) {
+ return;
+ }
+
+ if ((Mask & LogMask) == 0) {
+ return;
+ }
+
+ irql = KeGetCurrentIrql();
+
+ if (irql < DISPATCH_LEVEL) {
+ KeAcquireSpinLock(&LogSpinLock, &irql);
+ } else {
+ KeAcquireSpinLockAtDpcLevel(&LogSpinLock);
+ }
+
+ if (SerenumLPtr > SerenumLStart) {
+ SerenumLPtr -= 1; // Decrement to next entry
+ } else {
+ SerenumLPtr = SerenumLEnd;
+ }
+
+ sig.l = Sig;
+ rsig.b.Byte0 = sig.b.Byte3;
+ rsig.b.Byte1 = sig.b.Byte2;
+ rsig.b.Byte2 = sig.b.Byte1;
+ rsig.b.Byte3 = sig.b.Byte0;
+
+ SerenumLPtr->le_sig = rsig.l;
+ SerenumLPtr->le_info1 = Info1;
+ SerenumLPtr->le_info2 = Info2;
+ SerenumLPtr->le_info3 = Info3;
+
+ ASSERT(SerenumLPtr >= SerenumLStart);
+
+ if (irql < DISPATCH_LEVEL) {
+ KeReleaseSpinLock(&LogSpinLock, irql);
+ } else {
+ KeReleaseSpinLockFromDpcLevel(&LogSpinLock);
+ }
+
+ return;
+}
+
+
+VOID
+SerenumLogInit()
+/*++
+
+Routine Description:
+
+ Init the debug log - remember interesting information in a circular buffer
+
+Arguments:
+
+Return Value:
+
+ None.
+
+--*/
+{
+#ifdef MAX_DEBUG
+ ULONG logSize = 4096*6;
+#else
+ ULONG logSize = 4096*3;
+#endif
+
+
+ KeInitializeSpinLock(&LogSpinLock);
+
+ SerenumLStart = ExAllocatePoolWithTag(NonPagedPoolNx, logSize, 'mneS');
+
+ if (SerenumLStart) {
+ SerenumLPtr = SerenumLStart;
+
+ // Point the end (and first entry) 1 entry from the end of the segment
+ SerenumLEnd = SerenumLStart + (logSize
+ / sizeof(struct SERENUM_LOG_ENTRY))
+ - 1;
+ }
+
+ return;
+}
+
+VOID
+SerenumLogFree(
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+ None.
+
+--*/
+{
+ if (SerenumLStart) {
+ ExFreePoolWithTag(SerenumLStart,SERENUM_POOL_TAG);
+ }
+
+ return;
+}
+
+#endif // DBG
diff --git a/serial/serenum/log.h b/serial/serenum/log.h
new file mode 100644
index 00000000..d9cea5de
--- /dev/null
+++ b/serial/serenum/log.h
@@ -0,0 +1,57 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+:ts=4
+
+Module Name:
+
+ log.h
+
+Abstract:
+
+ debug macros
+
+Environment:
+
+ Kernel & user mode
+
+Revision History:
+
+ 10-27-95 : created
+
+--*/
+
+#ifndef __LOG_H__
+#define __LOG_H__
+
+
+#define LOG_MISC 0x00000001 //debug log entries
+#define LOG_ENUM 0x00000002
+#define LOG_PASSTHROUGH 0x00000004
+
+//
+// Assert Macros
+//
+
+#if DBG
+#define LOGENTRY(mask, sig, info1, info2, info3) \
+ SerenumDebugLogEntry(mask, sig, (ULONG_PTR)info1, \
+ (ULONG_PTR)info2, \
+ (ULONG_PTR)info3)
+
+VOID
+SerenumDebugLogEntry(IN ULONG Mask, IN ULONG Sig, IN ULONG_PTR Info1,
+ IN ULONG_PTR Info2, IN ULONG_PTR Info3);
+
+VOID
+SerenumLogInit();
+
+VOID
+SerenumLogFree();
+
+#else
+#define LOGENTRY(mask, sig, info1, info2, info3)
+#endif
+
+
+#endif // __LOG_H__
diff --git a/serial/serenum/pch.h b/serial/serenum/pch.h
new file mode 100644
index 00000000..d691138d
--- /dev/null
+++ b/serial/serenum/pch.h
@@ -0,0 +1,15 @@
+//
+// Copyright (C) Microsoft. All rights reserved.
+//
+
+#include <ntddk.h>
+#include <devioctl.h>
+#include <initguid.h>
+#include <wdmguid.h>
+#include <ntddser.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <ntstrsafe.h>
+#include "log.h"
+#include "serenum.h"
+
diff --git a/serial/serenum/pchsrc.c b/serial/serenum/pchsrc.c
new file mode 100644
index 00000000..17305716
--- /dev/null
+++ b/serial/serenum/pchsrc.c
@@ -0,0 +1 @@
+#include "pch.h" \ No newline at end of file
diff --git a/serial/serenum/pnp.c b/serial/serenum/pnp.c
new file mode 100644
index 00000000..878c9a7a
--- /dev/null
+++ b/serial/serenum/pnp.c
@@ -0,0 +1,1505 @@
+/*++
+Copyright (c) 1998-2000 Microsoft Corporation
+
+Module Name:
+
+ PNP.C
+
+Abstract:
+
+ This module contains contains the plugplay calls
+ PNP / WDM BUS driver.
+
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+
+--*/
+
+#include "pch.h"
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text (PAGE, Serenum_AddDevice)
+#pragma alloc_text (PAGE, Serenum_PnP)
+#pragma alloc_text (PAGE, Serenum_FDO_PnP)
+#pragma alloc_text (PAGE, Serenum_PDO_PnP)
+#pragma alloc_text (PAGE, Serenum_PnPRemove)
+#pragma alloc_text (PAGE, SerenumStartDeviceWorker)
+//#pragma alloc_text (PAGE, Serenum_Remove)
+#endif
+
+// disable warnings
+
+#if _MSC_VER >= 1200
+#pragma warning(push)
+#endif
+
+#pragma warning(disable:4127) // conditional expression is constant
+
+NTSTATUS
+Serenum_AddDevice(IN PDRIVER_OBJECT DriverObject,
+ IN PDEVICE_OBJECT BusPhysicalDeviceObject)
+/*++
+Routine Description.
+ A bus has been found. Attach our FDO to it.
+ Allocate any required resources. Set things up. And be prepared for the
+ first ``start device.''
+
+Arguments:
+ BusPhysicalDeviceObject - Device object representing the bus. That to which
+ we attach a new FDO.
+
+ DriverObject - This very self referenced driver.
+
+--*/
+{
+ NTSTATUS status;
+ PDEVICE_OBJECT deviceObject;
+ PFDO_DEVICE_DATA pDeviceData;
+ HANDLE keyHandle;
+ ULONG actualLength;
+ PCOMMON_DEVICE_DATA commonData;
+
+ PAGED_CODE();
+
+ Serenum_KdPrint_Def(SER_DBG_PNP_TRACE, ("Add Device: 0x%p\n",
+ BusPhysicalDeviceObject));
+ //
+ // Create our FDO
+ //
+
+ status = IoCreateDevice(DriverObject, sizeof(FDO_DEVICE_DATA), NULL,
+ FILE_DEVICE_BUS_EXTENDER, 0, TRUE, &deviceObject);
+
+ if (NT_SUCCESS(status)) {
+ pDeviceData = (PFDO_DEVICE_DATA)deviceObject->DeviceExtension;
+ RtlFillMemory (pDeviceData, sizeof (FDO_DEVICE_DATA), 0);
+
+ pDeviceData->IsFDO = TRUE;
+ pDeviceData->DebugLevel = SER_DEFAULT_DEBUG_OUTPUT_LEVEL;
+ pDeviceData->Self = deviceObject;
+ pDeviceData->AttachedPDO = NULL;
+ pDeviceData->NumPDOs = 0;
+ pDeviceData->DeviceState = PowerDeviceD0;
+ pDeviceData->SystemState = PowerSystemWorking;
+ pDeviceData->PDOForcedRemove = FALSE;
+
+ pDeviceData->SystemWake=PowerSystemUnspecified;
+ pDeviceData->DeviceWake=PowerDeviceUnspecified;
+
+ pDeviceData->Removed = FALSE;
+ commonData=(PCOMMON_DEVICE_DATA)deviceObject->DeviceExtension;
+ IoInitializeRemoveLock(&commonData->RemoveLock, SERENUM_POOL_TAG, 0, 0);
+
+
+ //
+ // Set the PDO for use with PlugPlay functions
+ //
+
+ pDeviceData->UnderlyingPDO = BusPhysicalDeviceObject;
+
+
+ //
+ // Attach our filter driver to the device stack.
+ // the return value of IoAttachDeviceToDeviceStack is the top of the
+ // attachment chain. This is where all the IRPs should be routed.
+ //
+ // Our filter will send IRPs to the top of the stack and use the PDO
+ // for all PlugPlay functions.
+ //
+
+ pDeviceData->TopOfStack
+ = IoAttachDeviceToDeviceStack(deviceObject, BusPhysicalDeviceObject);
+
+ if (!pDeviceData->TopOfStack) {
+ Serenum_KdPrint(pDeviceData, SER_DBG_PNP_ERROR,
+ ("AddDevice: IoAttach failed (%x)", status));
+ IoDeleteDevice(deviceObject);
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ //
+ // Set the type of IO we do
+ //
+
+ if (pDeviceData->TopOfStack->Flags & DO_BUFFERED_IO) {
+ deviceObject->Flags |= DO_BUFFERED_IO;
+ } else if (pDeviceData->TopOfStack->Flags & DO_DIRECT_IO) {
+ deviceObject->Flags |= DO_DIRECT_IO;
+ }
+
+
+ KeInitializeSemaphore(&pDeviceData->CreateSemaphore, 1, 1);
+ KeInitializeSpinLock(&pDeviceData->EnumerationLock);
+
+
+
+ //
+ // Tell the PlugPlay system that this device will need an interface
+ // device class shingle.
+ //
+ // It may be that the driver cannot hang the shingle until it starts
+ // the device itself, so that it can query some of its properties.
+ // (Aka the shingles guid (or ref string) is based on the properties
+ // of the device.)
+ //
+
+ status = IoRegisterDeviceInterface(BusPhysicalDeviceObject,
+ (LPGUID)&GUID_SERENUM_BUS_ENUMERATOR,
+ NULL,
+#pragma warning(suppress: 6014)
+ &pDeviceData->DevClassAssocName);
+ // pDeviceData->DevClassAssocName is freed when processing IRP_MN_REMOVE_DEVICE or in the error code paths of this routine
+ if (!NT_SUCCESS(status)) {
+ Serenum_KdPrint(pDeviceData, SER_DBG_PNP_ERROR,
+ ("AddDevice: IoRegisterDCA failed (%x)", status));
+
+ IoDetachDevice(pDeviceData->TopOfStack);
+ IoDeleteDevice(deviceObject);
+ return status;
+ }
+
+ //
+ // If for any reason you need to save values in a safe location that
+ // clients of this DeviceClassAssociate might be interested in reading
+ // here is the time to do so, with the function
+ // IoOpenDeviceClassRegistryKey
+ // the symbolic link name used is was returned in
+ // pDeviceData->DevClassAssocName (the same name which is returned by
+ // IoGetDeviceClassAssociations and the SetupAPI equivs.
+ //
+
+#if DBG
+ {
+ PWCHAR deviceName = NULL;
+ ULONG nameLength = 0;
+
+ status = IoGetDeviceProperty(BusPhysicalDeviceObject,
+ DevicePropertyPhysicalDeviceObjectName, 0,
+ NULL, &nameLength);
+
+ if ((nameLength != 0) && (status == STATUS_BUFFER_TOO_SMALL)) {
+ deviceName = ExAllocatePoolWithTag(NonPagedPoolNx, nameLength,SERENUM_POOL_TAG);
+
+ if (NULL == deviceName) {
+ goto someDebugStuffExit;
+ }
+
+ IoGetDeviceProperty(BusPhysicalDeviceObject,
+ DevicePropertyPhysicalDeviceObjectName,
+ nameLength, deviceName, &nameLength);
+
+ Serenum_KdPrint(pDeviceData, SER_DBG_PNP_TRACE,
+ ("AddDevice: %p to %p->%p (%ws) \n", deviceObject,
+ pDeviceData->TopOfStack, BusPhysicalDeviceObject,
+ deviceName));
+ }
+
+ someDebugStuffExit:;
+ if (deviceName != NULL) {
+ ExFreePoolWithTag(deviceName,SERENUM_POOL_TAG);
+ }
+ }
+#endif // DBG
+
+ //
+ // Turn on the shingle and point it to the given device object.
+ //
+ status = IoSetDeviceInterfaceState(&pDeviceData->DevClassAssocName,
+ TRUE);
+
+ if (!NT_SUCCESS(status)) {
+ Serenum_KdPrint(pDeviceData, SER_DBG_PNP_ERROR,
+ ("AddDevice: IoSetDeviceClass failed (%x)", status));
+
+ RtlFreeUnicodeString(&pDeviceData->DevClassAssocName);
+ return status;
+ }
+
+ //
+ // Open the regW2stry and read in our settings
+ //
+
+ status = IoOpenDeviceRegistryKey(pDeviceData->UnderlyingPDO,
+ PLUGPLAY_REGKEY_DEVICE,
+ STANDARD_RIGHTS_READ, &keyHandle);
+
+ if (status == STATUS_SUCCESS) {
+ status
+ = Serenum_GetRegistryKeyValue(keyHandle, L"SkipEnumerations",
+ sizeof(L"SkipEnumerations"),
+ &pDeviceData->SkipEnumerations,
+ sizeof(pDeviceData->SkipEnumerations),
+ &actualLength);
+
+ if ((status != STATUS_SUCCESS)
+ || (actualLength != sizeof(pDeviceData->SkipEnumerations))) {
+ pDeviceData->SkipEnumerations = 0;
+ status = STATUS_SUCCESS;
+
+ }
+
+ ZwClose(keyHandle);
+ }
+ }
+
+ if (NT_SUCCESS(status)) {
+ deviceObject->Flags |= DO_POWER_PAGABLE;
+ deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
+
+ }
+
+ return status;
+}
+
+
+NTSTATUS
+Serenum_PnP(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
+/*++
+Routine Description:
+ Answer the plethora of Irp Major PnP IRPS.
+--*/
+{
+ PIO_STACK_LOCATION irpStack;
+ NTSTATUS status;
+ PCOMMON_DEVICE_DATA commonData;
+
+ PAGED_CODE();
+
+ irpStack = IoGetCurrentIrpStackLocation(Irp);
+
+ ASSERT(irpStack->MajorFunction == IRP_MJ_PNP);
+
+ commonData = (PCOMMON_DEVICE_DATA)DeviceObject->DeviceExtension;
+ status=IoAcquireRemoveLock(&commonData->RemoveLock,Irp);
+ if(!NT_SUCCESS(status)){
+ Irp->IoStatus.Information=0;
+ Irp->IoStatus.Status=status;
+ IoCompleteRequest(Irp,IO_NO_INCREMENT);
+ Serenum_KdPrint(commonData, SER_DBG_PNP_TRACE,
+ ("PNP: removed DO: %p got IRP: %p\n", DeviceObject,
+ Irp));
+ return status;
+ }
+
+
+
+ //
+ // Call either the FDO or PDO Pnp code
+ //
+
+ if (commonData->IsFDO) {
+ Serenum_KdPrint(commonData, SER_DBG_PNP_TRACE,
+ ("PNP: Functional DO: %p IRP: %p MJ: %X MIN: %X\n",
+ DeviceObject, Irp, irpStack->MajorFunction,
+ irpStack->MinorFunction));
+
+ status = Serenum_FDO_PnP(DeviceObject, Irp, irpStack,
+ (PFDO_DEVICE_DATA)commonData);
+ goto PnPDone;
+
+ }
+
+ //
+ // PDO
+ //
+
+ Serenum_KdPrint(commonData, SER_DBG_PNP_TRACE,
+ ("PNP: Physical DO: %p IRP: %p MJ: %X MIN: %X\n",
+ DeviceObject, Irp, irpStack->MajorFunction,
+ irpStack->MinorFunction));
+
+ status = Serenum_PDO_PnP(DeviceObject, Irp, irpStack,
+ (PPDO_DEVICE_DATA)commonData);
+
+PnPDone:;
+ return status;
+}
+
+
+NTSTATUS
+SerenumCheckEnumerations(IN PFDO_DEVICE_DATA PFdoData)
+{
+
+ KIRQL oldIrql;
+ NTSTATUS status;
+ PIRP pIrp;
+ BOOLEAN sameDevice = TRUE;
+
+ Serenum_KdPrint(PFdoData, SER_DBG_PNP_TRACE, ("Checking enumerations"));
+
+ //
+ // If appropriate, check for new devices or if old devices still there.
+ //
+
+ if (PFdoData->SkipEnumerations == 0) {
+
+ KeAcquireSpinLock(&PFdoData->EnumerationLock, &oldIrql);
+
+ if (PFdoData->EnumFlags == SERENUM_ENUMFLAG_CLEAN) {
+ Serenum_KdPrint(PFdoData, SER_DBG_PNP_TRACE, ("EnumFlag Clean"));
+
+ //
+ // If nothing is going on, kick off an enumeration
+ //
+
+ PFdoData->EnumFlags |= SERENUM_ENUMFLAG_PENDING;
+ KeReleaseSpinLock(&PFdoData->EnumerationLock, oldIrql);
+
+
+ status = SerenumStartProtocolThread(PFdoData);
+ } else if ((PFdoData->EnumFlags
+ & (SERENUM_ENUMFLAG_REMOVED | SERENUM_ENUMFLAG_PENDING))
+ == SERENUM_ENUMFLAG_REMOVED) {
+ Serenum_KdPrint(PFdoData, SER_DBG_PNP_TRACE, ("EnumFlag Removed"));
+ //
+ // Clear the flag and do it synchronously to make sure we
+ // get the exact current state
+ //
+
+ PFdoData->EnumFlags &= ~SERENUM_ENUMFLAG_REMOVED;
+
+ KeReleaseSpinLock(&PFdoData->EnumerationLock, oldIrql);
+
+ pIrp = IoAllocateIrp(PFdoData->TopOfStack->StackSize + 1, FALSE);
+
+ if (pIrp == NULL) {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ goto SerenumCheckEnumerationsOut;
+ }
+
+ IoSetNextIrpStackLocation(pIrp);
+ status = Serenum_ReenumerateDevices(pIrp, PFdoData, &sameDevice);
+
+#pragma prefast(suppress:__WARNING_REDUNDANT_POINTER_TEST __WARNING_REDUNDANT_POINTER_TEST_FAR_EVIDENCE, "pIrp cannot be NULL; see IoSetNextIrpStackLocation")
+ if (pIrp != NULL) {
+ IoFreeIrp(pIrp);
+ }
+
+ KeAcquireSpinLock(&PFdoData->EnumerationLock, &oldIrql);
+
+ if (status == STATUS_SUCCESS) {
+ PFdoData->AttachedPDO = PFdoData->NewPDO;
+ PFdoData->PdoData = PFdoData->NewPdoData;
+ PFdoData->NumPDOs = PFdoData->NewNumPDOs;
+ PFdoData->PDOForcedRemove = PFdoData->NewPDOForcedRemove;
+ }
+
+ KeReleaseSpinLock(&PFdoData->EnumerationLock, oldIrql);
+
+ } else if (PFdoData->EnumFlags & SERENUM_ENUMFLAG_DIRTY) {
+
+ Serenum_KdPrint(PFdoData, SER_DBG_PNP_TRACE, ("EnumFlag Dirty"));
+
+ //
+ // If there is a new value, use the new values
+ //
+
+ PFdoData->AttachedPDO = PFdoData->NewPDO;
+ PFdoData->PdoData = PFdoData->NewPdoData;
+ PFdoData->NumPDOs = PFdoData->NewNumPDOs;
+ PFdoData->PDOForcedRemove = PFdoData->NewPDOForcedRemove;
+ PFdoData->EnumFlags &= ~SERENUM_ENUMFLAG_DIRTY;
+
+ KeReleaseSpinLock(&PFdoData->EnumerationLock, oldIrql);
+ status = STATUS_SUCCESS;
+ } else {
+ Serenum_KdPrint(PFdoData, SER_DBG_PNP_TRACE, ("EnumFlag default"));
+
+ //
+ // Use the current values
+ //
+
+ KeReleaseSpinLock(&PFdoData->EnumerationLock, oldIrql);
+ status = STATUS_SUCCESS;
+ }
+
+ } else {
+ status = STATUS_SUCCESS;
+
+ if (PFdoData->SkipEnumerations != 0xffffffff) {
+ PFdoData->SkipEnumerations--;
+ }
+ }
+
+SerenumCheckEnumerationsOut:
+
+ return status;
+}
+
+
+
+
+NTSTATUS
+Serenum_FDO_PnP (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpStack,
+ IN PFDO_DEVICE_DATA DeviceData
+ )
+/*++
+Routine Description:
+ Handle requests from the PlugPlay system for the BUS itself
+
+ NB: the various Minor functions of the PlugPlay system will not be
+ overlapped and do not have to be reentrant
+
+--*/
+{
+ NTSTATUS status;
+ KEVENT event;
+ ULONG length;
+ ULONG i;
+ PDEVICE_RELATIONS relations;
+
+ PCOMMON_DEVICE_DATA commonData=(PCOMMON_DEVICE_DATA)DeviceObject->DeviceExtension;
+ PIO_WORKITEM SerenumStartDeviceWorkItem = NULL;
+
+
+ PAGED_CODE();
+
+ switch (IrpStack->MinorFunction) {
+ case IRP_MN_START_DEVICE:
+ //
+ // BEFORE you are allowed to ``touch'' the device object to which
+ // the FDO is attached (that send an irp from the bus to the Device
+ // object to which the bus is attached). You must first pass down
+ // the start IRP. It might not be powered on, or able to access or
+ // something.
+ //
+
+ Serenum_KdPrint (DeviceData, SER_DBG_PNP_TRACE, ("Start Device\n"));
+
+ if (DeviceData->Started){
+ Serenum_KdPrint (DeviceData, SER_DBG_PNP_TRACE,
+ ("Device already started\n"));
+ status = STATUS_SUCCESS;
+ break;
+ }
+
+ // if we get here we know the device is stopped
+ IoCopyCurrentIrpStackLocationToNext (Irp);
+ SerenumStartDeviceWorkItem = IoAllocateWorkItem(DeviceObject);
+ if (SerenumStartDeviceWorkItem==NULL){
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ break;
+ }
+
+ IoMarkIrpPending(Irp);
+ IoSetCompletionRoutine (Irp,
+ SerenumStartDeviceCompletion,
+ SerenumStartDeviceWorkItem,
+ TRUE,
+ TRUE,
+ TRUE);
+
+ IoCallDriver (DeviceData->TopOfStack, Irp);
+ status = STATUS_PENDING;
+ return status;
+ break;
+
+ case IRP_MN_QUERY_STOP_DEVICE:
+ Serenum_KdPrint (DeviceData, SER_DBG_PNP_TRACE,
+ ("Query Stop Device\n"));
+
+ //
+ // Test to see if there are any PDO created as children of this FDO
+ // If there are then conclude the device is busy and fail the
+ // query stop.
+ //
+ // CIMEXCIMEX
+ // We could do better, by seing if the children PDOs are actually
+ // currently open. If they are not then we could stop, get new
+ // resouces, fill in the new resouce values, and then when a new client
+ // opens the PDO use the new resources. But this works for now.
+ //
+
+ if (DeviceData->AttachedPDO
+ || (DeviceData->EnumFlags & SERENUM_ENUMFLAG_PENDING)) {
+ status = STATUS_UNSUCCESSFUL;
+
+ } else {
+ status = STATUS_SUCCESS;
+ }
+
+ Irp->IoStatus.Status = status;
+
+ if (NT_SUCCESS(status)) {
+ IoSkipCurrentIrpStackLocation (Irp);
+ status = IoCallDriver (DeviceData->TopOfStack, Irp);
+ } else {
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+ }
+
+ IoReleaseRemoveLock(&commonData->RemoveLock,Irp);
+ return status;
+
+ case IRP_MN_CANCEL_STOP_DEVICE:
+ //
+ // We always succeed a cancel stop
+ //
+
+ Serenum_KdPrint (DeviceData, SER_DBG_PNP_TRACE,
+ ("Cancel Stop Device\n"));
+
+ Irp->IoStatus.Status = STATUS_SUCCESS;
+ IoSkipCurrentIrpStackLocation(Irp);
+ status = IoCallDriver(DeviceData->TopOfStack, Irp);
+
+ IoReleaseRemoveLock(&commonData->RemoveLock,Irp);
+ return status;
+
+ case IRP_MN_STOP_DEVICE:
+ Serenum_KdPrint (DeviceData, SER_DBG_PNP_TRACE, ("Stop Device\n"));
+
+ //
+ // Wait for the enum thread to complete if it's running
+ //
+
+ SerenumWaitForEnumThreadTerminate(DeviceData);
+
+ //
+ // After the start IRP has been sent to the lower driver object, the
+ // bus may NOT send any more IRPS down ``touch'' until another START
+ // has occured.
+ // What ever access is required must be done before the Irp is passed
+ // on.
+ //
+ // Stop device means that the resources given durring Start device
+ // are no revoked. So we need to stop using them
+ //
+
+ DeviceData->Started = FALSE;
+
+ //
+ // We don't need a completion routine so fire and forget.
+ //
+ // Set the current stack location to the next stack location and
+ // call the next device object.
+ //
+ Irp->IoStatus.Status = STATUS_SUCCESS;
+ IoSkipCurrentIrpStackLocation (Irp);
+ status = IoCallDriver (DeviceData->TopOfStack, Irp);
+
+ IoReleaseRemoveLock(&commonData->RemoveLock,Irp);
+ return status;
+
+ case IRP_MN_REMOVE_DEVICE:
+ Serenum_KdPrint (DeviceData, SER_DBG_PNP_TRACE, ("Remove Device\n"));
+
+ //
+ // The PlugPlay system has detected the removal of this device. We
+ // have no choice but to detach and delete the device object.
+ // (If we wanted to express and interest in preventing this removal,
+ // we should have filtered the query remove and query stop routines.)
+ //
+ // Note! we might receive a remove WITHOUT first receiving a stop.
+ // ASSERT (!DeviceData->Removed);
+
+
+ //
+ // Synchronize with the enum thread if it is running and wait
+ // for it to finish
+ //
+
+
+ SerenumWaitForEnumThreadTerminate(DeviceData);
+
+ // block until all outstanding IO has returned
+ //
+ // Wait for all outstanding requests to complete
+ //
+ Serenum_KdPrint (DeviceData, SER_DBG_PNP_TRACE,("Waiting for outstanding requests\n"));
+ // We will accept no new requests
+ //
+ DeviceData->Removed = TRUE;
+
+ //
+ // Complete any outstanding IRPs queued by the driver here.
+ //
+
+ //
+ // Make the DCA go away. Some drivers may choose to remove the DCA
+ // when they receive a stop or even a query stop. We just don't care.
+ //
+ (void)IoSetDeviceInterfaceState (&DeviceData->DevClassAssocName, FALSE);
+
+ //
+ // Here if we had any outstanding requests in a personal queue we should
+ // complete them all now.
+ //
+ // Note, the device is guarenteed stopped, so we cannot send it any non-
+ // PNP IRPS.
+ //
+
+ //
+ // Fire and forget
+ //
+ Irp->IoStatus.Status = STATUS_SUCCESS;
+ IoSkipCurrentIrpStackLocation (Irp);
+ status = IoCallDriver (DeviceData->TopOfStack, Irp);
+
+ IoReleaseRemoveLockAndWait(&commonData->RemoveLock,Irp);
+ //
+ // Free the associated resources
+ //
+
+ //
+ // Detach from the underlying devices.
+ //
+ Serenum_KdPrint(DeviceData, SER_DBG_PNP_INFO,
+ ("IoDetachDevice: 0x%p\n", DeviceData->TopOfStack));
+ IoDetachDevice (DeviceData->TopOfStack);
+
+ //
+ // Clean up any resources here
+ //
+
+
+ ExFreePoolWithTag (DeviceData->DevClassAssocName.Buffer,SERENUM_POOL_TAG);
+ // DeviceData->DevClassAssocName was initialized in AddDevice
+#pragma warning(suppress: 6001)
+ RtlFreeUnicodeString(&DeviceData->DevClassAssocName);
+ Serenum_KdPrint(DeviceData, SER_DBG_PNP_INFO,
+ ("IoDeleteDevice: 0x%p\n", DeviceObject));
+
+ //
+ // Remove any PDO's we ejected
+ //
+
+ if (DeviceData->AttachedPDO != NULL) {
+ ASSERT(DeviceData->NumPDOs == 1);
+
+ Serenum_PnPRemove(DeviceData->AttachedPDO, DeviceData->PdoData);
+ DeviceData->PdoData = NULL;
+ DeviceData->AttachedPDO = NULL;
+ DeviceData->NumPDOs = 0;
+ }
+
+ IoDeleteDevice(DeviceObject);
+
+ return status;
+
+ case IRP_MN_QUERY_DEVICE_RELATIONS:
+ if (BusRelations != IrpStack->Parameters.QueryDeviceRelations.Type) {
+ //
+ // We don't support this
+ //
+ Serenum_KdPrint (DeviceData, SER_DBG_PNP_TRACE,
+ ("Query Device Relations - Non bus\n"));
+ goto SER_FDO_PNP_DEFAULT;
+ }
+
+ Serenum_KdPrint (DeviceData, SER_DBG_PNP_TRACE,
+ ("Query Bus Relations\n"));
+
+ status = SerenumCheckEnumerations(DeviceData);
+
+ if(!NT_SUCCESS(status))
+ {
+ Irp->IoStatus.Status = status;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+ IoReleaseRemoveLock(&commonData->RemoveLock,Irp);
+ return status;
+ }
+ //
+ // Tell the plug and play system about all the PDOs.
+ //
+ // There might also be device relations below and above this FDO,
+ // so, be sure to propagate the relations from the upper drivers.
+ //
+ // No Completion routine is needed so long as the status is preset
+ // to success. (PDOs complete plug and play irps with the current
+ // IoStatus.Status and IoStatus.Information as the default.)
+ //
+
+ //KeAcquireSpinLock (&DeviceData->Spin, &oldIrq);
+
+ i = (0 == Irp->IoStatus.Information) ? 0 :
+ ((PDEVICE_RELATIONS) Irp->IoStatus.Information)->Count;
+ // The current number of PDOs in the device relations structure
+
+ length = sizeof(DEVICE_RELATIONS) +
+ ((DeviceData->NumPDOs + i) * sizeof (PDEVICE_OBJECT));
+
+ relations = (PDEVICE_RELATIONS) ExAllocatePoolWithTag (NonPagedPoolNx, length,SERENUM_POOL_TAG);
+
+ if (NULL == relations) {
+ Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+ IoReleaseRemoveLock(&commonData->RemoveLock,Irp);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ //
+ // Copy in the device objects so far
+ //
+ if (i) {
+ RtlCopyMemory (
+ relations->Objects,
+ ((PDEVICE_RELATIONS) Irp->IoStatus.Information)->Objects,
+ i * sizeof (PDEVICE_OBJECT));
+ }
+ relations->Count = DeviceData->NumPDOs + i;
+
+ //
+ // For each PDO on this bus add a pointer to the device relations
+ // buffer, being sure to take out a reference to that object.
+ // The PlugPlay system will dereference the object when it is done with
+ // it and free the device relations buffer.
+ //
+
+ if (DeviceData->NumPDOs) {
+ relations->Objects[relations->Count-1] = DeviceData->AttachedPDO;
+ ObReferenceObject (DeviceData->AttachedPDO);
+ }
+
+ //
+ // Set up and pass the IRP further down the stack
+ //
+ Irp->IoStatus.Status = STATUS_SUCCESS;
+
+ if (0 != Irp->IoStatus.Information) {
+ ExFreePoolWithTag ((PVOID) Irp->IoStatus.Information,SERENUM_POOL_TAG);
+ }
+ Irp->IoStatus.Information = (ULONG_PTR)relations;
+
+ IoSkipCurrentIrpStackLocation (Irp);
+ status = IoCallDriver (DeviceData->TopOfStack, Irp);
+
+ IoReleaseRemoveLock(&commonData->RemoveLock,Irp);
+
+ return status;
+
+ case IRP_MN_QUERY_REMOVE_DEVICE:
+ //
+ // If we were to fail this call then we would need to complete the
+ // IRP here. Since we are not, set the status to SUCCESS and
+ // call the next driver.
+ //
+
+ Serenum_KdPrint (DeviceData, SER_DBG_PNP_TRACE,
+ ("Query Remove Device\n"));
+
+ //
+ // Wait for the enum thread to complete if it's running
+ //
+ SerenumWaitForEnumThreadTerminate(DeviceData);
+
+
+ Irp->IoStatus.Status = STATUS_SUCCESS;
+ IoSkipCurrentIrpStackLocation (Irp);
+ status = IoCallDriver (DeviceData->TopOfStack, Irp);
+ IoReleaseRemoveLock(&commonData->RemoveLock,Irp);
+ return status;
+
+
+ case IRP_MN_QUERY_CAPABILITIES: {
+
+ PIO_STACK_LOCATION irpSp;
+
+ //
+ // Send this down to the PDO first
+ //
+
+ KeInitializeEvent (&event, NotificationEvent, FALSE);
+ IoCopyCurrentIrpStackLocationToNext (Irp);
+
+ IoSetCompletionRoutine (Irp,
+ SerenumSyncCompletion,
+ &event,
+ TRUE,
+ TRUE,
+ TRUE);
+
+ status = IoCallDriver (DeviceData->TopOfStack, Irp);
+
+ if (STATUS_PENDING == status) {
+ // wait for it...
+
+ status = KeWaitForSingleObject (&event,
+ Executive,
+ KernelMode,
+ FALSE, // Not allertable
+ NULL); // No timeout structure
+#if DBG
+ ASSERT (STATUS_SUCCESS == status);
+#endif
+ if(!NT_SUCCESS(status)){
+ Irp->IoStatus.Status=status;
+ IoReleaseRemoveLock(&commonData->RemoveLock,Irp);
+ IoCompleteRequest (Irp, IO_NO_INCREMENT);
+ return status;
+ }
+ status = Irp->IoStatus.Status;
+ }
+
+ if (NT_SUCCESS(status)) {
+
+ irpSp = IoGetCurrentIrpStackLocation(Irp);
+
+ DeviceData->SystemWake
+ = irpSp->Parameters.DeviceCapabilities.Capabilities->SystemWake;
+ DeviceData->DeviceWake
+ = irpSp->Parameters.DeviceCapabilities.Capabilities->DeviceWake;
+ }
+
+ break;
+ }
+
+
+
+SER_FDO_PNP_DEFAULT:
+ default:
+ //
+ // In the default case we merely call the next driver since
+ // we don't know what to do.
+ //
+ Serenum_KdPrint (DeviceData, SER_DBG_PNP_TRACE, ("Default Case\n"));
+
+ //
+ // Fire and Forget
+ //
+ IoSkipCurrentIrpStackLocation (Irp);
+
+ //
+ // Done, do NOT complete the IRP, it will be processed by the lower
+ // device object, which will complete the IRP
+ //
+
+ status = IoCallDriver (DeviceData->TopOfStack, Irp);
+ IoReleaseRemoveLock(&commonData->RemoveLock,Irp);
+ return status;
+ }
+
+ Irp->IoStatus.Status = status;
+ IoCompleteRequest (Irp, IO_NO_INCREMENT);
+
+ IoReleaseRemoveLock(&commonData->RemoveLock,Irp);
+ return status;
+}
+
+VOID
+SerenumMarkPdoRemoved(PFDO_DEVICE_DATA PFdoData)
+{
+ KIRQL oldIrql;
+
+ KeAcquireSpinLock(&PFdoData->EnumerationLock, &oldIrql);
+
+ PFdoData->EnumFlags |= SERENUM_ENUMFLAG_REMOVED;
+
+ KeReleaseSpinLock(&PFdoData->EnumerationLock, oldIrql);
+}
+
+
+NTSTATUS
+Serenum_PDO_PnP (IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpStack, IN PPDO_DEVICE_DATA DeviceData)
+/*++
+Routine Description:
+ Handle requests from the PlugPlay system for the devices on the BUS
+
+--*/
+{
+ PDEVICE_CAPABILITIES deviceCapabilities;
+ PWCHAR buffer;
+ ULONG length;
+ NTSTATUS status;
+ PWCHAR returnBuffer = NULL;
+ PCOMMON_DEVICE_DATA commonData=(PCOMMON_DEVICE_DATA)DeviceObject->DeviceExtension;
+
+
+ PAGED_CODE();
+
+ status = Irp->IoStatus.Status;
+
+ //
+ // NB: since we are a bus enumerator, we have no one to whom we could
+ // defer these irps. Therefore we do not pass them down but merely
+ // return them.
+ //
+
+ switch (IrpStack->MinorFunction) {
+ case IRP_MN_QUERY_CAPABILITIES:
+
+ Serenum_KdPrint (DeviceData, SER_DBG_PNP_TRACE, ("Query Caps \n"));
+
+ //
+ // Get the packet.
+ //
+
+ deviceCapabilities=IrpStack->Parameters.DeviceCapabilities.Capabilities;
+
+ //
+ // Set the capabilities.
+ //
+
+ deviceCapabilities->Version = 1;
+ deviceCapabilities->Size = sizeof (DEVICE_CAPABILITIES);
+
+ //
+ // We cannot wake the system.
+ //
+
+ deviceCapabilities->SystemWake
+ = ((PFDO_DEVICE_DATA)DeviceData->ParentFdo->DeviceExtension)
+ ->SystemWake;
+ deviceCapabilities->DeviceWake
+ = ((PFDO_DEVICE_DATA)DeviceData->ParentFdo->DeviceExtension)
+ ->DeviceWake;
+
+ //
+ // We have no latencies
+ //
+
+ deviceCapabilities->D1Latency = 0;
+ deviceCapabilities->D2Latency = 0;
+ deviceCapabilities->D3Latency = 0;
+
+ deviceCapabilities->UniqueID = FALSE;
+ status = STATUS_SUCCESS;
+ break;
+
+ case IRP_MN_QUERY_DEVICE_TEXT: {
+ if ((IrpStack->Parameters.QueryDeviceText.DeviceTextType
+ != DeviceTextDescription) || DeviceData->DevDesc.Buffer == NULL) {
+ break;
+ }
+
+ returnBuffer = ExAllocatePoolWithTag(PagedPool, DeviceData->DevDesc.Length,SERENUM_POOL_TAG);
+
+ if (returnBuffer == NULL) {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ break;
+ }
+
+ status = STATUS_SUCCESS;
+
+ RtlCopyMemory(returnBuffer, DeviceData->DevDesc.Buffer,
+ DeviceData->DevDesc.Length);
+
+ Serenum_KdPrint(DeviceData, SER_DBG_PNP_TRACE,
+ ("TextID: buf 0x%p\n", returnBuffer));
+
+ Irp->IoStatus.Information = (ULONG_PTR)returnBuffer;
+ break;
+ }
+
+
+ case IRP_MN_QUERY_ID:
+ //
+ // Query the IDs of the device
+ //
+
+ Serenum_KdPrint(DeviceData, SER_DBG_PNP_TRACE,
+ ("QueryID: 0x%x\n", IrpStack->Parameters.QueryId.IdType));
+
+ switch (IrpStack->Parameters.QueryId.IdType) {
+
+
+ case BusQueryInstanceID:
+ //
+ // Build an instance ID. This is what PnP uses to tell if it has
+ // seen this thing before or not. Build it from the first hardware
+ // id and the port number.
+ //
+ // NB since we do not incorperate the port number
+ // this method does not produce unique ids;
+ //
+ // return 0000 for all devices and have the flag set to not unique
+ //
+
+ status = STATUS_SUCCESS;
+
+ length = SERENUM_INSTANCE_IDS_LENGTH * sizeof(WCHAR);
+ returnBuffer = ExAllocatePoolWithTag(PagedPool, length,SERENUM_POOL_TAG);
+
+ if (returnBuffer != NULL) {
+ RtlCopyMemory(returnBuffer, SERENUM_INSTANCE_IDS, length);
+ } else {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ Serenum_KdPrint(DeviceData, SER_DBG_PNP_TRACE,
+ ("InstanceID: buf 0x%p\n", returnBuffer));
+
+ Irp->IoStatus.Information = (ULONG_PTR)returnBuffer;
+ break;
+
+
+ //
+ // The other ID's we just copy from the buffers and are done.
+ //
+
+ case BusQueryDeviceID:
+ case BusQueryHardwareIDs:
+ case BusQueryCompatibleIDs:
+ {
+ PUNICODE_STRING pId = NULL;
+ status = STATUS_SUCCESS;
+
+ switch (IrpStack->Parameters.QueryId.IdType) {
+ case BusQueryDeviceID:
+ pId = &DeviceData->DeviceIDs;
+ break;
+
+ case BusQueryHardwareIDs:
+ pId = &DeviceData->HardwareIDs;
+ break;
+
+ case BusQueryCompatibleIDs:
+ pId = &DeviceData->CompIDs;
+ break;
+ }
+
+ buffer = pId ? pId->Buffer : NULL;
+
+ if (buffer != NULL) {
+ length = pId->Length;
+ returnBuffer = ExAllocatePoolWithTag(PagedPool, length + sizeof(WCHAR),SERENUM_POOL_TAG);
+ if (returnBuffer != NULL) {
+ RtlZeroMemory(returnBuffer, length + sizeof(WCHAR) );
+ RtlCopyMemory(returnBuffer, buffer, length);
+
+ } else {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+ }
+
+ Serenum_KdPrint(DeviceData, SER_DBG_PNP_TRACE,
+ ("ID: Unicode 0x%p\n", pId));
+ Serenum_KdPrint(DeviceData, SER_DBG_PNP_TRACE,
+ ("ID: buf 0x%p\n", returnBuffer));
+
+ Irp->IoStatus.Information = (ULONG_PTR)returnBuffer;
+ }
+ break;
+ }
+ break;
+
+ case IRP_MN_QUERY_BUS_INFORMATION: {
+ PPNP_BUS_INFORMATION pBusInfo;
+
+ ASSERTMSG("Serenum appears not to be the sole bus?!?",
+ Irp->IoStatus.Information == (ULONG_PTR)NULL);
+
+ pBusInfo = ExAllocatePoolWithTag(PagedPool, sizeof(PNP_BUS_INFORMATION),SERENUM_POOL_TAG);
+
+ if (pBusInfo == NULL) {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ break;
+ }
+
+ pBusInfo->BusTypeGuid = GUID_BUS_TYPE_SERENUM;
+ pBusInfo->LegacyBusType = PNPBus;
+
+ //
+ // We really can't track our bus number since we can be torn
+ // down with our bus
+ //
+
+ pBusInfo->BusNumber = 0;
+
+ Irp->IoStatus.Information = (ULONG_PTR)pBusInfo;
+ status = STATUS_SUCCESS;
+ break;
+ }
+
+ case IRP_MN_QUERY_DEVICE_RELATIONS:
+ switch (IrpStack->Parameters.QueryDeviceRelations.Type) {
+ case TargetDeviceRelation: {
+ PDEVICE_RELATIONS pDevRel;
+
+ //
+ // No one else should respond to this since we are the PDO
+ //
+
+ ASSERT(Irp->IoStatus.Information == 0);
+
+ if (Irp->IoStatus.Information != 0) {
+ break;
+ }
+
+
+ pDevRel = ExAllocatePoolWithTag(PagedPool, sizeof(DEVICE_RELATIONS),SERENUM_POOL_TAG);
+
+ if (pDevRel == NULL) {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ break;
+ }
+
+ pDevRel->Count = 1;
+ pDevRel->Objects[0] = DeviceObject;
+ ObReferenceObject(DeviceObject);
+
+ status = STATUS_SUCCESS;
+ Irp->IoStatus.Information = (ULONG_PTR)pDevRel;
+ break;
+ }
+
+
+ default:
+ break;
+ }
+
+ break;
+
+ case IRP_MN_START_DEVICE:
+
+ //
+ // Save serial number and PnPRev
+ //
+
+ if(DeviceData->PnPRev.Length || DeviceData->SerialNo.Length) {
+ UNICODE_STRING keyname;
+ HANDLE pnpKey;
+
+ status = IoOpenDeviceRegistryKey(DeviceObject, PLUGPLAY_REGKEY_DEVICE,
+ STANDARD_RIGHTS_WRITE, &pnpKey);
+ if (!NT_SUCCESS(status)) {
+ Serenum_KdPrint(DeviceData, SER_DBG_PNP_TRACE,
+ ("DoOpenDeviceRegistryKey failed (%x)\n", status));
+ //
+ // We failed on opening a handle to registry key. The failure is
+ // not so intense that we should fail the IRP_MN_START_DEVICE,
+ // thus we set the status to STATUS_SUCCESS and continue.
+ //
+ status = STATUS_SUCCESS;
+ break;
+ }
+
+ if(DeviceData->PnPRev.Length) {
+ RtlInitUnicodeString(&keyname, NULL);
+ keyname.MaximumLength = sizeof(L"PnPRev");
+ keyname.Buffer = ExAllocatePoolWithTag(PagedPool, keyname.MaximumLength,SERENUM_POOL_TAG);
+
+ if (keyname.Buffer != NULL) {
+
+ RtlAppendUnicodeToString(&keyname, L"PnPRev");
+ status = ZwSetValueKey(pnpKey, &keyname, 0, REG_SZ, DeviceData->PnPRev.Buffer, DeviceData->PnPRev.Length+sizeof(WCHAR));
+ if(NT_SUCCESS(status)){
+ Serenum_KdPrint(DeviceData, SER_DBG_PNP_TRACE,
+ ("ZwSetValueKey succeeded (%x)\n", status));
+ }else{
+ Serenum_KdPrint(DeviceData, SER_DBG_PNP_TRACE,
+ ("ZwSetValueKey failed (%x)\n", status));
+ }
+ ExFreePoolWithTag(keyname.Buffer,SERENUM_POOL_TAG);
+ }
+ }
+ if(DeviceData->SerialNo.Length) {
+ RtlInitUnicodeString(&keyname, NULL);
+ keyname.MaximumLength = sizeof(L"Serial Number");
+ keyname.Buffer = ExAllocatePoolWithTag(PagedPool, keyname.MaximumLength,SERENUM_POOL_TAG);
+
+ if (keyname.Buffer != NULL) {
+
+ RtlAppendUnicodeToString(&keyname, L"Serial Number");
+ status = ZwSetValueKey(pnpKey, &keyname, 0, REG_SZ, DeviceData->SerialNo.Buffer, DeviceData->SerialNo.Length+sizeof(WCHAR));
+ if(NT_SUCCESS(status)){
+ Serenum_KdPrint(DeviceData, SER_DBG_PNP_TRACE,
+ ("ZwSetValueKey succeeded (%x)\n", status));
+ }else{
+ Serenum_KdPrint(DeviceData, SER_DBG_PNP_TRACE,
+ ("ZwSetValueKey failed (%x)\n", status));
+ }
+ ExFreePoolWithTag(keyname.Buffer,SERENUM_POOL_TAG);
+
+ }
+ }
+
+ ZwClose(pnpKey);
+ }
+
+ DeviceData->Started = TRUE;
+ status = STATUS_SUCCESS;
+ break;
+
+ case IRP_MN_STOP_DEVICE:
+ Serenum_KdPrint(DeviceData, SER_DBG_PNP_TRACE, ("Stop Device\n"));
+
+ //
+ // Here we shut down the device. The opposite of start.
+ //
+
+ DeviceData->Started = FALSE;
+ status = STATUS_SUCCESS;
+ break;
+
+ case IRP_MN_REMOVE_DEVICE:
+ Serenum_KdPrint(DeviceData, SER_DBG_PNP_TRACE, ("Remove Device\n"));
+
+ //
+ // Mark as removed so we enumerate devices correctly -- this will
+ // cause the next enumeration request to occur synchronously
+ //
+
+ SerenumMarkPdoRemoved((PFDO_DEVICE_DATA)DeviceData->ParentFdo
+ ->DeviceExtension);
+
+ //
+ // Attached is only set to FALSE by the enumeration process.
+ //
+
+ if (!DeviceData->Attached) {
+
+ status = Serenum_PnPRemove(DeviceObject, DeviceData);
+ }
+ else {
+ //
+ // Succeed the remove
+ ///
+ status = STATUS_SUCCESS;
+ }
+ break;
+
+ case IRP_MN_QUERY_STOP_DEVICE:
+ Serenum_KdPrint(DeviceData, SER_DBG_PNP_TRACE, ("Query Stop Device\n"));
+
+ //
+ // No reason here why we can't stop the device.
+ // If there were a reason we should speak now for answering success
+ // here may result in a stop device irp.
+ //
+
+ status = STATUS_SUCCESS;
+ break;
+
+ case IRP_MN_CANCEL_STOP_DEVICE:
+ Serenum_KdPrint(DeviceData, SER_DBG_PNP_TRACE, ("Cancel Stop Device\n"));
+ //
+ // The stop was canceled. Whatever state we set, or resources we put
+ // on hold in anticipation of the forcoming STOP device IRP should be
+ // put back to normal. Someone, in the long list of concerned parties,
+ // has failed the stop device query.
+ //
+
+ status = STATUS_SUCCESS;
+ break;
+
+ case IRP_MN_QUERY_REMOVE_DEVICE:
+ Serenum_KdPrint(DeviceData, SER_DBG_PNP_TRACE, ("Query Remove Device\n"));
+ //
+ // Just like Query Stop only now the impending doom is the remove irp
+ //
+ status = STATUS_SUCCESS;
+ break;
+
+ case IRP_MN_CANCEL_REMOVE_DEVICE:
+ Serenum_KdPrint(DeviceData, SER_DBG_PNP_TRACE, ("Cancel Remove Device"
+ "\n"));
+ //
+ // Clean up a remove that did not go through, just like cancel STOP.
+ //
+ status = STATUS_SUCCESS;
+ break;
+
+ case IRP_MN_QUERY_RESOURCE_REQUIREMENTS:
+ case IRP_MN_READ_CONFIG:
+ case IRP_MN_WRITE_CONFIG: // we have no config space
+ case IRP_MN_EJECT:
+ case IRP_MN_SET_LOCK:
+ case IRP_MN_QUERY_INTERFACE: // We do not have any non IRP based interfaces.
+ default:
+ Serenum_KdPrint(DeviceData, SER_DBG_PNP_TRACE, ("PNP Not handled 0x%x\n",
+ IrpStack->MinorFunction));
+ // For PnP requests to the PDO that we do not understand we should
+ // return the IRP WITHOUT setting the status or information fields.
+ // They may have already been set by a filter (eg acpi).
+ break;
+ }
+
+ Irp->IoStatus.Status = status;
+ IoCompleteRequest (Irp, IO_NO_INCREMENT);
+ IoReleaseRemoveLock(&commonData->RemoveLock,Irp);
+ return status;
+}
+
+NTSTATUS
+Serenum_PnPRemove (PDEVICE_OBJECT Device, PPDO_DEVICE_DATA PdoData)
+/*++
+Routine Description:
+ The PlugPlay subsystem has instructed that this PDO should be removed.
+
+ We should therefore
+ - Complete any requests queued in the driver
+ - If the device is still attached to the system,
+ then complete the request and return.
+ - Otherwise, cleanup device specific allocations, memory, events...
+ - Call IoDeleteDevice
+ - Return from the dispatch routine.
+
+ Note that if the device is still connected to the bus (IE in this case
+ the control panel has not yet told us that the serial device has
+ disappeared) then the PDO must remain around, and must be returned during
+ any query Device relaions IRPS.
+
+--*/
+
+{
+ PAGED_CODE();
+
+ Serenum_KdPrint(PdoData, SER_DBG_PNP_TRACE,
+ ("Serenum_PnPRemove: 0x%p\n", Device));
+
+ //
+ // Complete any outstanding requests with STATUS_DELETE_PENDING.
+ //
+ // Serenum does not queue any irps at this time so we have nothing to do.
+ //
+
+ if (PdoData->Attached || PdoData->Removed) {
+ return STATUS_SUCCESS;
+ }
+
+ PdoData->Removed = TRUE;
+
+ //
+ // Free any resources.
+ //
+
+ RtlFreeUnicodeString(&PdoData->HardwareIDs);
+ RtlFreeUnicodeString(&PdoData->CompIDs);
+ RtlFreeUnicodeString(&PdoData->DeviceIDs);
+
+ Serenum_KdPrint(PdoData, SER_DBG_PNP_INFO,
+ ("IoDeleteDevice: 0x%p\n", Device));
+
+ IoDeleteDevice(Device);
+
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+SerenumStartDeviceCompletion(
+ PDEVICE_OBJECT DeviceObject,
+ PIRP Irp,
+ PVOID Context
+ )
+/*++
+
+Routine Description:
+
+ This routine triggers the event associated with the block semantics of the
+ IRP_MN_START_DEVICE
+
+Arguments:
+
+ DeviceObject - Targeted device object
+ Irp - IRP_MN_START_DEVICE irp
+ Context - PIO_WORKITEM workItem
+
+Return Value:
+
+ STATUS_MORE_PROCESSING_REQUIRED
+
+--*/
+{
+ PCOMMON_DEVICE_DATA commonData=(PCOMMON_DEVICE_DATA)DeviceObject->DeviceExtension;
+ PIO_WORKITEM workItem = (PIO_WORKITEM)Context;
+ NTSTATUS status;
+
+ _Analysis_assume_(workItem != NULL); // Not NULL when passed to IoQueueWorkItem()
+
+ status = Irp->IoStatus.Status;
+
+ if (NT_SUCCESS(status)){
+ IoQueueWorkItemEx(workItem,
+ SerenumStartDeviceWorker,
+ DelayedWorkQueue,
+ Irp);
+ return STATUS_MORE_PROCESSING_REQUIRED;
+ }
+ else
+ {
+ IoFreeWorkItem(workItem);
+ }
+ IoReleaseRemoveLock( &commonData->RemoveLock,Irp );
+ return STATUS_CONTINUE_COMPLETION;
+}
+
+
+
+VOID SerenumStartDeviceWorker(
+ PVOID deviceObject,
+ PVOID irp,
+ PIO_WORKITEM WorkItem
+ )
+{
+
+ PDEVICE_OBJECT DeviceObject=(PDEVICE_OBJECT)deviceObject;
+ PIRP Irp=(PIRP)irp;
+ PCOMMON_DEVICE_DATA commonData=(PCOMMON_DEVICE_DATA)DeviceObject->DeviceExtension;
+ PFDO_DEVICE_DATA DeviceData=(PFDO_DEVICE_DATA)DeviceObject->DeviceExtension;
+ PRTL_QUERY_REGISTRY_TABLE QueryTable = NULL;
+ ULONG DebugLevelDefault = SER_DEFAULT_DEBUG_OUTPUT_LEVEL;
+ PAGED_CODE();
+
+ _Analysis_assume_(Irp != NULL); // Not NULL when passed to IoQueueWorkItem()
+
+ if (NULL == (QueryTable = ExAllocatePoolWithTag(
+ PagedPool,
+ sizeof(RTL_QUERY_REGISTRY_TABLE)*2,
+ SERENUM_POOL_TAG
+ ))){
+ Serenum_KdPrint (DeviceData, SER_DBG_PNP_ERROR,
+ ("Failed to allocate memory to query registy\n"));
+ DeviceData->DebugLevel = DebugLevelDefault;
+ // This Irp is passed as the context parameter
+ // This is the same Irp which is sent by the kernel to the completion routine
+ Irp->IoStatus.Status=STATUS_INSUFFICIENT_RESOURCES;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+ IoFreeWorkItem(WorkItem);
+ IoReleaseRemoveLock( &commonData->RemoveLock,Irp);
+ return;
+ }else{
+ RtlZeroMemory(QueryTable,sizeof(RTL_QUERY_REGISTRY_TABLE)*2);
+ QueryTable[0].QueryRoutine = NULL;
+ QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
+ QueryTable[0].EntryContext = &commonData->DebugLevel;
+ QueryTable[0].Name = L"DebugLevel";
+ QueryTable[0].DefaultType = REG_DWORD;
+ QueryTable[0].DefaultData = &DebugLevelDefault;
+ QueryTable[0].DefaultLength= sizeof(ULONG);
+
+ // CIMEXCIMEX: The rest of the table isn't filled in!
+
+ if (!NT_SUCCESS(RtlQueryRegistryValues(
+ RTL_REGISTRY_SERVICES,
+ L"Serenum",
+ QueryTable,
+ NULL,
+ NULL))){
+ Serenum_KdPrint (DeviceData,SER_DBG_PNP_ERROR,
+ ("Failed to get debug level from registry. "
+ "Using default\n"));
+ DeviceData->DebugLevel = DebugLevelDefault;
+ }
+ ExFreePoolWithTag(QueryTable,SERENUM_POOL_TAG);
+
+ }
+
+ Serenum_KdPrint (DeviceData, SER_DBG_PNP_TRACE,
+ ("Start Device: Device started successfully\n"));
+ DeviceData->Started = TRUE;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+ IoFreeWorkItem(WorkItem);
+ IoReleaseRemoveLock( &commonData->RemoveLock,Irp);
+ return;
+}
+
+
+
+#if _MSC_VER >= 1200
+#pragma warning(pop)
+#endif
+
+#pragma warning(default:4127) // conditional expression is constant
+
+
+
diff --git a/serial/serenum/power.c b/serial/serenum/power.c
new file mode 100644
index 00000000..65ff5ff2
--- /dev/null
+++ b/serial/serenum/power.c
@@ -0,0 +1,299 @@
+/*++
+Copyright (c) 1997 Microsoft Corporation
+
+Module Name:
+
+ POWER.C
+
+Abstract:
+
+ This module contains contains the power calls for the serenum bus driver.
+
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+
+--*/
+
+#include "pch.h"
+
+#ifdef ALLOC_PRAGMA
+//#pragma alloc_text (PAGE, Serenum_Power)
+//#pragma alloc_text (PAGE, Serenum_FDO_Power)
+//#pragma alloc_text (PAGE, Serenum_PDO_Power)
+#endif
+
+IO_COMPLETION_ROUTINE
+Serenum_FDOPowerComplete;
+
+NTSTATUS
+Serenum_FDOPowerComplete (IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp,
+ IN PVOID Context)
+/*++
+--*/
+{
+ POWER_STATE powerState;
+ POWER_STATE_TYPE powerType;
+ PIO_STACK_LOCATION stack;
+ PFDO_DEVICE_DATA data;
+ PCOMMON_DEVICE_DATA commonData=(PCOMMON_DEVICE_DATA)DeviceObject->DeviceExtension;
+
+ UNREFERENCED_PARAMETER(Context);
+
+
+ if (Irp->PendingReturned) {
+ IoMarkIrpPending(Irp);
+ }
+
+
+ data = (PFDO_DEVICE_DATA)DeviceObject->DeviceExtension;
+ stack = IoGetCurrentIrpStackLocation(Irp);
+ powerType = stack->Parameters.Power.Type;
+ powerState = stack->Parameters.Power.State;
+
+ switch (stack->MinorFunction) {
+ case IRP_MN_SET_POWER:
+ switch (powerType) {
+ case DevicePowerState:
+ //
+ // Powering Up
+ //
+ ASSERT(powerState.DeviceState < data->DeviceState);
+ data->DeviceState = powerState.DeviceState;
+
+ PoSetPowerState(data->Self, powerType, powerState);
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case IRP_MN_QUERY_POWER:
+ ASSERT(IRP_MN_QUERY_POWER != stack->MinorFunction);
+ break;
+
+ default:
+ //
+ // Basically, this is ASSERT(0)
+ //
+ ASSERT(0xBADBAD == IRP_MN_QUERY_POWER);
+ break;
+ }
+
+
+ PoStartNextPowerIrp(Irp);
+ IoReleaseRemoveLock(&commonData->RemoveLock,Irp);
+
+ return STATUS_SUCCESS; // Continue completion...
+}
+
+NTSTATUS
+Serenum_FDO_Power(PFDO_DEVICE_DATA Data, PIRP Irp)
+/*++
+--*/
+{
+ NTSTATUS status;
+ BOOLEAN hookit = FALSE;
+ POWER_STATE powerState;
+ POWER_STATE_TYPE powerType;
+ PIO_STACK_LOCATION stack;
+ PCOMMON_DEVICE_DATA commonData=(PCOMMON_DEVICE_DATA)Data;
+
+ stack = IoGetCurrentIrpStackLocation(Irp);
+ powerType = stack->Parameters.Power.Type;
+ powerState = stack->Parameters.Power.State;
+
+
+
+ switch (stack->MinorFunction) {
+ case IRP_MN_SET_POWER:
+ //
+ // If it hasn't started, we just pass it through
+ //
+
+ if (Data->Started != TRUE) {
+#pragma warning(suppress: 28931) // this status is returned by the power dispatch routine
+ status = Irp->IoStatus.Status = STATUS_SUCCESS;
+ break;
+ }
+
+ Serenum_KdPrint(Data, SER_DBG_PNP_TRACE,
+ ("Serenum-PnP Setting %s state to %d\n",
+ ((powerType == SystemPowerState) ? "System" : "Device"),
+ powerState.SystemState));
+
+ switch (powerType) {
+ case DevicePowerState:
+#pragma warning(suppress: 28931) // this status is returned by the power dispatch routine
+ status = Irp->IoStatus.Status = STATUS_SUCCESS;
+
+ if (Data->DeviceState < powerState.DeviceState) {
+ PoSetPowerState (Data->Self, powerType, powerState);
+ Data->DeviceState = powerState.DeviceState;
+ } else if (Data->DeviceState > powerState.DeviceState) {
+ //
+ // Powering Up
+ //
+ hookit = TRUE;
+ }
+
+ break;
+
+ case SystemPowerState:
+ //
+ // status should be STATUS_SUCCESS
+ //
+ break;
+ }
+ break;
+
+ case IRP_MN_QUERY_POWER:
+#pragma warning(suppress: 28931) // this status is returned by the power dispatch routine
+ status = Irp->IoStatus.Status = STATUS_SUCCESS;
+ break;
+
+ default:
+ //
+ // status should be STATUS_SUCCESS
+ //
+ break;
+ }
+
+ IoCopyCurrentIrpStackLocationToNext (Irp);
+
+ if (hookit) {
+ IoSetCompletionRoutine(Irp, Serenum_FDOPowerComplete, NULL, TRUE, TRUE,
+ TRUE);
+
+ status = PoCallDriver (Data->TopOfStack, Irp);
+
+ } else {
+ PoStartNextPowerIrp (Irp);
+ status = PoCallDriver (Data->TopOfStack, Irp);
+ IoReleaseRemoveLock(&commonData->RemoveLock,Irp);
+ }
+
+
+ return status;
+}
+
+NTSTATUS
+Serenum_PDO_Power (
+ PPDO_DEVICE_DATA PdoData,
+ PIRP Irp
+ )
+/*++
+--*/
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PIO_STACK_LOCATION stack;
+ POWER_STATE powerState;
+ POWER_STATE_TYPE powerType;
+ PCOMMON_DEVICE_DATA commonData=(PCOMMON_DEVICE_DATA)PdoData;
+
+ stack = IoGetCurrentIrpStackLocation (Irp);
+ powerType = stack->Parameters.Power.Type;
+ powerState = stack->Parameters.Power.State;
+#pragma warning(suppress: 28931) // this status is returned by the power dispatch routine
+ switch (stack->MinorFunction) {
+ case IRP_MN_SET_POWER:
+ switch (powerType) {
+ case DevicePowerState:
+ if (PdoData->DeviceState > powerState.DeviceState) {
+ PoSetPowerState (PdoData->Self, powerType, powerState);
+ PdoData->DeviceState = powerState.DeviceState;
+ } else if (PdoData->DeviceState < powerState.DeviceState) {
+ //
+ // Powering down.
+ //
+ PoSetPowerState (PdoData->Self, powerType, powerState);
+ PdoData->DeviceState = powerState.DeviceState;
+ }
+ break;
+
+ case SystemPowerState:
+ //
+ // Default to STATUS_SUCCESS
+ //
+ break;
+
+ default:
+ status = STATUS_NOT_IMPLEMENTED;
+ break;
+ }
+ break;
+
+ case IRP_MN_QUERY_POWER:
+ //
+ // Default to STATUS_SUCCESS
+ //
+ break;
+
+ case IRP_MN_WAIT_WAKE:
+ case IRP_MN_POWER_SEQUENCE:
+ status = STATUS_NOT_IMPLEMENTED;
+ break;
+
+ default:
+ status = Irp->IoStatus.Status;
+ }
+
+ Irp->IoStatus.Status = status;
+ PoStartNextPowerIrp (Irp);
+ IoCompleteRequest (Irp, IO_NO_INCREMENT);
+ IoReleaseRemoveLock(&commonData->RemoveLock,Irp);
+ return status;
+}
+
+NTSTATUS
+Serenum_Power (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+/*++
+--*/
+{
+#if DBG
+ PIO_STACK_LOCATION irpStack;
+#endif
+ NTSTATUS status;
+ PCOMMON_DEVICE_DATA commonData;
+
+
+ commonData = (PCOMMON_DEVICE_DATA) DeviceObject->DeviceExtension;
+ status=IoAcquireRemoveLock(&commonData->RemoveLock,Irp);
+ if(!NT_SUCCESS(status)){
+ Irp->IoStatus.Information=0;
+ Irp->IoStatus.Status=status;
+ IoCompleteRequest(Irp,IO_NO_INCREMENT);
+ // The Device Object has been removed
+ //PoStartNextPowerIrp(Irp);
+ return status;
+ }
+
+#if DBG
+
+ irpStack = IoGetCurrentIrpStackLocation (Irp);
+ ASSERT (IRP_MJ_POWER == irpStack->MajorFunction);
+
+#endif
+ if (commonData->IsFDO) {
+ status =
+ Serenum_FDO_Power ((PFDO_DEVICE_DATA) DeviceObject->DeviceExtension,
+ Irp);
+ } else {
+ status =
+ Serenum_PDO_Power ((PPDO_DEVICE_DATA) DeviceObject->DeviceExtension,
+ Irp);
+ }
+
+ return status;
+}
+
+
+
diff --git a/serial/serenum/serenum.c b/serial/serenum/serenum.c
new file mode 100644
index 00000000..0e369528
--- /dev/null
+++ b/serial/serenum/serenum.c
@@ -0,0 +1,766 @@
+/*++
+Copyright (c) 1997-1998 Microsoft Corporation
+
+Module Name:
+
+ SERENUM.C
+
+Abstract:
+
+ This module contains contains the entry points for a standard bus
+ PNP / WDM driver.
+
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+
+
+--*/
+
+#include "pch.h"
+
+//
+// Declare some entry functions as pageable, and make DriverEntry
+// discardable
+//
+
+DRIVER_INITIALIZE DriverEntry;
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(INIT, DriverEntry)
+#pragma alloc_text(PAGE, Serenum_DriverUnload)
+#endif
+
+// disable warnings
+
+#if _MSC_VER >= 1200
+#pragma warning(push)
+#endif
+
+#pragma warning(disable:4127) // conditional expression is constant
+
+NTSTATUS
+DriverEntry (
+ IN PDRIVER_OBJECT DriverObject,
+ IN PUNICODE_STRING UniRegistryPath
+ )
+/*++
+Routine Description:
+
+ Initialize the entry points of the driver.
+
+--*/
+{
+ ULONG i;
+
+ UNREFERENCED_PARAMETER (UniRegistryPath);
+
+ Serenum_KdPrint_Def (SER_DBG_SS_TRACE, ("Driver Entry\n"));
+ Serenum_KdPrint_Def (SER_DBG_SS_TRACE, ("RegPath: %p\n", UniRegistryPath));
+
+ //
+ // Set ever slot to initially pass the request through to the lower
+ // device object
+ //
+ for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) {
+ DriverObject->MajorFunction[i] = Serenum_DispatchPassThrough;
+ }
+
+ //
+ // Fill in the Dispatch slots intercepted by the filter driver.
+ //
+ DriverObject->MajorFunction [IRP_MJ_CREATE] =
+ DriverObject->MajorFunction [IRP_MJ_CLOSE] = Serenum_CreateClose;
+ DriverObject->MajorFunction [IRP_MJ_PNP] = Serenum_PnP;
+ DriverObject->MajorFunction [IRP_MJ_POWER] = Serenum_Power;
+ DriverObject->MajorFunction [IRP_MJ_DEVICE_CONTROL] = Serenum_IoCtl;
+ DriverObject->MajorFunction [IRP_MJ_INTERNAL_DEVICE_CONTROL]
+ = Serenum_InternIoCtl;
+ DriverObject->DriverUnload = Serenum_DriverUnload;
+ DriverObject->DriverExtension->AddDevice = Serenum_AddDevice;
+
+
+#if DBG
+ SerenumLogInit();
+#endif
+
+ return STATUS_SUCCESS;
+}
+
+IO_COMPLETION_ROUTINE
+SerenumSyncCompletion;
+
+NTSTATUS
+SerenumSyncCompletion(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp,
+ IN PVOID Context)
+{
+ PKEVENT SerenumSyncEvent = (PKEVENT) Context;
+
+ UNREFERENCED_PARAMETER(DeviceObject);
+ UNREFERENCED_PARAMETER(Irp);
+
+ _Analysis_assume_(SerenumSyncEvent != NULL);
+
+ KeSetEvent(SerenumSyncEvent, IO_NO_INCREMENT, FALSE);
+ return STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+NTSTATUS
+Serenum_CreateClose(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
+/*++
+Routine Description:
+ Some outside source is trying to create a file against us.
+
+ If this is for the FDO (the bus itself) then the caller is trying to
+ open the propriatary conection to tell us which serial port to enumerate.
+
+ If this is for the PDO (an object on the bus) then this is a client that
+ wishes to use the serial port.
+--*/
+{
+ PIO_STACK_LOCATION irpStack;
+ NTSTATUS status = STATUS_SUCCESS;
+ KEVENT completionEvent;
+ PDEVICE_OBJECT pNextDevice;
+ PCOMMON_DEVICE_DATA commonData=(PCOMMON_DEVICE_DATA)DeviceObject->DeviceExtension;
+
+
+ UNREFERENCED_PARAMETER(DeviceObject);
+ status=IoAcquireRemoveLock(&commonData->RemoveLock,Irp);
+ if(!NT_SUCCESS(status)){
+ Irp->IoStatus.Information=0;
+ Irp->IoStatus.Status=status;
+ IoCompleteRequest(Irp,IO_NO_INCREMENT);
+ return status;
+ }
+
+
+ irpStack = IoGetCurrentIrpStackLocation(Irp);
+
+ if (commonData->IsFDO) {
+ pNextDevice = ((PFDO_DEVICE_DATA)DeviceObject->DeviceExtension)
+ ->TopOfStack;
+ } else {
+ pNextDevice = ((PFDO_DEVICE_DATA)((PPDO_DEVICE_DATA)DeviceObject->
+ DeviceExtension)->ParentFdo->
+ DeviceExtension)->TopOfStack;
+ }
+
+ switch (irpStack->MajorFunction) {
+ case IRP_MJ_CREATE:
+ Serenum_KdPrint_Def(SER_DBG_SS_TRACE, ("Create"));
+
+ //
+ // Pass on the create and the close
+ //
+
+ status = Serenum_DispatchPassThroughWithoutAcquire(DeviceObject, Irp);
+ break;
+
+ case IRP_MJ_CLOSE:
+ Serenum_KdPrint_Def (SER_DBG_SS_TRACE, ("Close \n"));
+
+ //
+ // Send the close down; after it finishes we can open and take
+ // over the port
+ //
+
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+
+ KeInitializeEvent(&completionEvent, SynchronizationEvent, FALSE);
+
+ IoSetCompletionRoutine(Irp, SerenumSyncCompletion, &completionEvent,
+ TRUE, TRUE, TRUE);
+
+ status = IoCallDriver(pNextDevice, Irp);
+
+
+ if (status == STATUS_PENDING) {
+ KeWaitForSingleObject(&completionEvent, Executive, KernelMode, FALSE,
+ NULL);
+ }
+
+ status = Irp->IoStatus.Status;
+
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+ IoReleaseRemoveLock(&commonData->RemoveLock, Irp);
+
+ break;
+ }
+
+ return status;
+}
+
+
+NTSTATUS
+Serenum_IoCtl (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+/*++
+Routine Description:
+
+--*/
+{
+ PIO_STACK_LOCATION irpStack;
+ NTSTATUS status;
+ PCOMMON_DEVICE_DATA commonData;
+ PFDO_DEVICE_DATA fdoData;
+ HANDLE keyHandle;
+ ULONG actualLength;
+
+
+
+ status = STATUS_SUCCESS;
+ irpStack = IoGetCurrentIrpStackLocation (Irp);
+ ASSERT (IRP_MJ_DEVICE_CONTROL == irpStack->MajorFunction);
+
+ commonData = (PCOMMON_DEVICE_DATA) DeviceObject->DeviceExtension;
+ fdoData = (PFDO_DEVICE_DATA) DeviceObject->DeviceExtension;
+
+ status = IoAcquireRemoveLock(&commonData->RemoveLock, Irp);
+ if (!NT_SUCCESS(status)){
+ Irp->IoStatus.Information = 0;
+ Irp->IoStatus.Status = status;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+ return status;
+ }
+
+
+ //buffer = Irp->AssociatedIrp.SystemBuffer;
+
+ //
+ // We only take Device Control requests for the FDO.
+ // That is the bus itself.
+ //
+ // The request is one of the propriatary Ioctls for
+ //
+ // NB We ARE a filter driver, so we DO pass on the irp if we don't handle it
+ //
+
+ //inlen = irpStack->Parameters.DeviceIoControl.InputBufferLength;
+ //outlen = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
+
+ if (!commonData->IsFDO) {
+ //
+ // These commands are only allowed to go to the FDO. Since they came
+ // into the PDO, we need to fire them down to the serenum Fdo.
+ //
+ return Serenum_Generic_FireAndRelease_RemoveLock(((PPDO_DEVICE_DATA) DeviceObject->DeviceExtension)->ParentFdo,Irp);
+ }
+
+ switch (irpStack->Parameters.DeviceIoControl.IoControlCode) {
+ case IOCTL_SERENUM_GET_PORT_NAME:
+ //
+ // Get the port name from the registry.
+ // This IOCTL is used by the modem cpl.
+ //
+
+ status = IoOpenDeviceRegistryKey(fdoData->UnderlyingPDO,
+ PLUGPLAY_REGKEY_DEVICE,
+ STANDARD_RIGHTS_READ,
+ &keyHandle);
+
+ if (!NT_SUCCESS(status)) {
+ //
+ // This is a fatal error. If we can't get to our registry key,
+ // we are sunk.
+ //
+ Serenum_KdPrint_Def (SER_DBG_PNP_ERROR,
+ ("IoOpenDeviceRegistryKey failed - %x \n", status));
+ Irp->IoStatus.Information = 0;
+ } else {
+ status = Serenum_GetRegistryKeyValue(
+ keyHandle,
+ L"PortName",
+ sizeof(L"PortName"),
+ Irp->AssociatedIrp.SystemBuffer,
+ irpStack->Parameters.DeviceIoControl.OutputBufferLength,
+ &actualLength);
+ if ( STATUS_OBJECT_NAME_NOT_FOUND == status ||
+ STATUS_INVALID_PARAMETER == status ) {
+ status = Serenum_GetRegistryKeyValue(
+ keyHandle,
+ L"Identifier",
+ sizeof(L"Identifier"),
+ Irp->AssociatedIrp.SystemBuffer,
+ irpStack->Parameters.DeviceIoControl.OutputBufferLength,
+ &actualLength);
+ }
+ Irp->IoStatus.Information = actualLength;
+ ZwClose (keyHandle);
+ }
+ break;
+ default:
+ //
+ // This is not intended for us - fire and Protect!
+ //
+ return Serenum_DispatchPassThroughWithoutAcquire(
+ DeviceObject,
+ Irp);
+ }
+
+ IoReleaseRemoveLock(&commonData->RemoveLock, Irp);
+ Irp->IoStatus.Status = status;
+ IoCompleteRequest (Irp, IO_NO_INCREMENT);
+ return status;
+}
+
+NTSTATUS
+Serenum_InternIoCtl (
+ PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+/*++
+Routine Description:
+
+--*/
+{
+ PIO_STACK_LOCATION irpStack;
+ NTSTATUS status;
+ PCOMMON_DEVICE_DATA commonData;
+ PPDO_DEVICE_DATA pdoData;
+
+// PAGED_CODE();
+
+
+ commonData = (PCOMMON_DEVICE_DATA) DeviceObject->DeviceExtension;
+ status=IoAcquireRemoveLock(&commonData->RemoveLock,Irp);
+ if(!NT_SUCCESS(status)){
+ Irp->IoStatus.Information=0;
+ Irp->IoStatus.Status=status;
+ IoCompleteRequest(Irp,IO_NO_INCREMENT);
+ return status;
+ }
+ status = STATUS_SUCCESS;
+ irpStack = IoGetCurrentIrpStackLocation (Irp);
+ ASSERT (IRP_MJ_INTERNAL_DEVICE_CONTROL == irpStack->MajorFunction);
+
+
+ pdoData = (PPDO_DEVICE_DATA) DeviceObject->DeviceExtension;
+
+ //
+ // We only take Internal Device Control requests for the PDO.
+ // That is the objects on the bus (representing the serial ports)
+ //
+ // We do pass on the irp if this comes into the fdo, but not if it comes
+ // into the pdo.
+ //
+
+ if (commonData->IsFDO) {
+ return Serenum_DispatchPassThroughWithoutAcquire(
+ DeviceObject,
+ Irp);
+ }else{
+ //buffer = Irp->UserBuffer;
+
+ switch (irpStack->Parameters.DeviceIoControl.IoControlCode) {
+ case IOCTL_INTERNAL_SERENUM_REMOVE_SELF:
+ Serenum_KdPrint(pdoData, SER_DBG_SS_TRACE, ("Remove self\n"));
+
+ ((PFDO_DEVICE_DATA) pdoData->ParentFdo->DeviceExtension)->
+ PDOForcedRemove = TRUE;
+ Serenum_PDO_EnumMarkMissing(pdoData->ParentFdo->DeviceExtension, DeviceObject->DeviceExtension);
+
+ IoInvalidateDeviceRelations(
+ ((PFDO_DEVICE_DATA) pdoData->ParentFdo->DeviceExtension)->
+ UnderlyingPDO,
+ BusRelations );
+ status = STATUS_SUCCESS;
+ Irp->IoStatus.Information = 0;
+ break;
+
+ default:
+ //
+ // Pass it through
+ //
+ return Serenum_DispatchPassThroughWithoutAcquire(DeviceObject, Irp);
+ }
+ }
+
+ Irp->IoStatus.Status = status;
+ IoCompleteRequest (Irp, IO_NO_INCREMENT);
+ IoReleaseRemoveLock(&commonData->RemoveLock,Irp);
+ return status;
+}
+
+
+VOID
+Serenum_DriverUnload (
+ IN PDRIVER_OBJECT Driver
+ )
+/*++
+Routine Description:
+ Clean up everything we did in driver entry.
+
+--*/
+{
+ UNREFERENCED_PARAMETER (Driver);
+ PAGED_CODE();
+
+ //
+ // All the device objects should be gone.
+ //
+
+ ASSERT (NULL == Driver->DeviceObject);
+
+ //
+ // Here we free any resources allocated in DriverEntry
+ //
+
+#if DBG
+ SerenumLogFree();
+#endif
+
+ return;
+}
+
+NTSTATUS
+Serenum_DispatchPassThrough(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+/*++
+Routine Description:
+
+ Passes a request on to the lower driver.
+
+--*/
+{
+ PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);
+ BOOLEAN waitForEnum = FALSE;
+ NTSTATUS rval;
+ PFDO_DEVICE_DATA pFdoData;
+ BOOLEAN isFdo;
+
+ PCOMMON_DEVICE_DATA commonData=(PCOMMON_DEVICE_DATA)DeviceObject->DeviceExtension;
+ isFdo = commonData->IsFDO;
+
+ rval=IoAcquireRemoveLock(&commonData->RemoveLock,Irp);
+ if(!NT_SUCCESS(rval)){
+ Irp->IoStatus.Information=0;
+ Irp->IoStatus.Status=rval;
+ IoCompleteRequest(Irp,IO_NO_INCREMENT);
+ return rval;
+ }
+
+ if (isFdo) {
+ pFdoData = (PFDO_DEVICE_DATA)DeviceObject->DeviceExtension;
+
+#if DBG
+ switch (IrpStack->MajorFunction) {
+ case IRP_MJ_READ:
+ LOGENTRY(LOG_PASSTHROUGH, 'SFRD', DeviceObject, Irp, 0);
+ break;
+
+ case IRP_MJ_WRITE:
+ LOGENTRY(LOG_PASSTHROUGH, 'SFWR', DeviceObject, Irp, 0);
+ break;
+
+ case IRP_MJ_DEVICE_CONTROL:
+ LOGENTRY(LOG_PASSTHROUGH, 'SFDC', DeviceObject, Irp, 0);
+ break;
+
+ case IRP_MJ_CREATE:
+ LOGENTRY(LOG_PASSTHROUGH, 'SFCR', DeviceObject, Irp, 0);
+ break;
+
+ case IRP_MJ_CLOSE:
+ LOGENTRY(LOG_PASSTHROUGH, 'SFCL', DeviceObject, Irp, 0);
+ break;
+
+ default:
+ break;
+ }
+#endif // DBG
+
+ } else {
+ pFdoData = ((PPDO_DEVICE_DATA) DeviceObject->DeviceExtension)->
+ ParentFdo->DeviceExtension;
+
+#if DBG
+ switch (IrpStack->MajorFunction) {
+ case IRP_MJ_READ:
+ LOGENTRY(LOG_PASSTHROUGH, 'SPRD', DeviceObject, Irp, 0);
+ break;
+
+ case IRP_MJ_WRITE:
+ LOGENTRY(LOG_PASSTHROUGH, 'SPWR', DeviceObject, Irp, 0);
+ break;
+
+ case IRP_MJ_DEVICE_CONTROL:
+ LOGENTRY(LOG_PASSTHROUGH, 'SPDC', DeviceObject, Irp, 0);
+ break;
+
+ case IRP_MJ_CREATE:
+ LOGENTRY(LOG_PASSTHROUGH, 'SPCR', DeviceObject, Irp, 0);
+ break;
+
+ case IRP_MJ_CLOSE:
+ LOGENTRY(LOG_PASSTHROUGH, 'SPCL', DeviceObject, Irp, 0);
+ break;
+
+ default:
+ break;
+ }
+#endif // DBG
+ }
+
+ if (IrpStack->MajorFunction == IRP_MJ_CREATE) {
+ //
+ // If we're doing an enumeration, we must wait here
+ //
+
+ waitForEnum = TRUE;
+
+ LOGENTRY(LOG_PASSTHROUGH, 'SPCW', DeviceObject, Irp, 0);
+
+ rval = KeWaitForSingleObject(&pFdoData->CreateSemaphore, Executive,
+ KernelMode, FALSE, NULL);
+ if (!NT_SUCCESS(rval)) {
+ LOGENTRY(LOG_PASSTHROUGH, 'SPCF', DeviceObject, Irp, rval);
+ Irp->IoStatus.Status = rval;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+ IoReleaseRemoveLock(&commonData->RemoveLock, Irp);
+ return rval;
+ }
+ }
+
+ //
+ // Pass the IRP to the target
+ //
+
+
+
+ if (waitForEnum) {
+ IoSkipCurrentIrpStackLocation (Irp);
+ rval = IoCallDriver(pFdoData->TopOfStack, Irp);
+ KeReleaseSemaphore(&pFdoData->CreateSemaphore, IO_NO_INCREMENT, 1, FALSE);
+ IoReleaseRemoveLock(&commonData->RemoveLock, Irp);
+ }else{
+ rval=Serenum_Generic_FireAndRelease_RemoveLock(pFdoData->TopOfStack,Irp);
+ }
+ return rval;
+}
+
+
+NTSTATUS Serenum_Generic_FireAndRelease_RemoveLock(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)
+{
+ NTSTATUS status;
+ // we should not block on start irp instead completion routine should do processing
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+ // use ExIoSetCompletionRoutineEx to make sure non pnp drivers are unloaded while completion routines could still have to run
+ IoSetCompletionRoutine(Irp,
+ (PIO_COMPLETION_ROUTINE)Serenum_CompletionRoutine,
+ NULL,
+ TRUE,
+ TRUE,
+ TRUE);
+ status = IoCallDriver(DeviceObject, Irp);
+ return status;
+}
+
+NTSTATUS
+Serenum_DispatchPassThroughWithoutAcquire(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+/*++
+Routine Description:
+
+ Passes a request on to the lower driver.
+
+--*/
+{
+ PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);
+ BOOLEAN waitForEnum = FALSE;
+ NTSTATUS rval;
+ PFDO_DEVICE_DATA pFdoData;
+ BOOLEAN isFdo;
+
+ PCOMMON_DEVICE_DATA commonData=(PCOMMON_DEVICE_DATA)DeviceObject->DeviceExtension;
+ isFdo = commonData->IsFDO;
+
+ if (isFdo) {
+ pFdoData = (PFDO_DEVICE_DATA)DeviceObject->DeviceExtension;
+
+#if DBG
+ switch (IrpStack->MajorFunction) {
+ case IRP_MJ_READ:
+ LOGENTRY(LOG_PASSTHROUGH, 'SFRD', DeviceObject, Irp, 0);
+ break;
+
+ case IRP_MJ_WRITE:
+ LOGENTRY(LOG_PASSTHROUGH, 'SFWR', DeviceObject, Irp, 0);
+ break;
+
+ case IRP_MJ_DEVICE_CONTROL:
+ LOGENTRY(LOG_PASSTHROUGH, 'SFDC', DeviceObject, Irp, 0);
+ break;
+
+ case IRP_MJ_CREATE:
+ LOGENTRY(LOG_PASSTHROUGH, 'SFCR', DeviceObject, Irp, 0);
+ break;
+
+ case IRP_MJ_CLOSE:
+ LOGENTRY(LOG_PASSTHROUGH, 'SFCL', DeviceObject, Irp, 0);
+ break;
+
+ default:
+ break;
+ }
+#endif // DBG
+
+ } else {
+ pFdoData = ((PPDO_DEVICE_DATA) DeviceObject->DeviceExtension)->ParentFdo->DeviceExtension;
+
+#if DBG
+ switch (IrpStack->MajorFunction) {
+ case IRP_MJ_READ:
+ LOGENTRY(LOG_PASSTHROUGH, 'SPRD', DeviceObject, Irp, 0);
+ break;
+
+ case IRP_MJ_WRITE:
+ LOGENTRY(LOG_PASSTHROUGH, 'SPWR', DeviceObject, Irp, 0);
+ break;
+
+ case IRP_MJ_DEVICE_CONTROL:
+ LOGENTRY(LOG_PASSTHROUGH, 'SPDC', DeviceObject, Irp, 0);
+ break;
+
+ case IRP_MJ_CREATE:
+ LOGENTRY(LOG_PASSTHROUGH, 'SPCR', DeviceObject, Irp, 0);
+ break;
+
+ case IRP_MJ_CLOSE:
+ LOGENTRY(LOG_PASSTHROUGH, 'SPCL', DeviceObject, Irp, 0);
+ break;
+
+ default:
+ break;
+ }
+#endif // DBG
+ }
+
+ if (IrpStack->MajorFunction == IRP_MJ_CREATE) {
+ //
+ // If we're doing an enumeration, we must wait here
+ //
+
+ waitForEnum = TRUE;
+
+ LOGENTRY(LOG_PASSTHROUGH, 'SPCW', DeviceObject, Irp, 0);
+
+ rval = KeWaitForSingleObject(&pFdoData->CreateSemaphore, Executive,
+ KernelMode, FALSE, NULL);
+ if (!NT_SUCCESS(rval)) {
+ LOGENTRY(LOG_PASSTHROUGH, 'SPCF', DeviceObject, Irp, rval);
+ Irp->IoStatus.Status = rval;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+ IoReleaseRemoveLock(&commonData->RemoveLock, Irp);
+ return rval;
+ }
+ }
+
+ //
+ // Pass the IRP to the target
+ //
+
+
+
+ if (waitForEnum){
+ IoSkipCurrentIrpStackLocation (Irp);
+ rval = IoCallDriver(pFdoData->TopOfStack, Irp);
+ KeReleaseSemaphore(&pFdoData->CreateSemaphore, IO_NO_INCREMENT, 1, FALSE);
+ IoReleaseRemoveLock(&commonData->RemoveLock, Irp);
+ }else{
+ rval=Serenum_Generic_FireAndRelease_RemoveLock(pFdoData->TopOfStack,Irp);
+ }
+ return rval;
+}
+
+
+
+void
+Serenum_InitPDO (
+ _In_ __drv_aliasesMem PDEVICE_OBJECT Pdo,
+ PFDO_DEVICE_DATA FdoData
+ )
+/*
+Description:
+ Common code to initialize a newly created serenum pdo.
+ Called either when the control panel exposes a device or when Serenum senses
+ a new device was attached.
+
+Parameters:
+ Pdo - The pdo
+ FdoData - The fdo's device extension
+*/
+{
+ ULONG FdoFlags = FdoData->Self->Flags;
+ PPDO_DEVICE_DATA pdoData = Pdo->DeviceExtension;
+ KIRQL oldIrql;
+
+ //
+ // Check the IO style
+ //
+ if (FdoFlags & DO_BUFFERED_IO) {
+ Pdo->Flags |= DO_BUFFERED_IO;
+ } else if (FdoFlags & DO_DIRECT_IO) {
+ Pdo->Flags |= DO_DIRECT_IO;
+ }
+
+ //
+ // Increment the pdo's stacksize so that it can pass irps through
+ //
+ Pdo->StackSize += FdoData->Self->StackSize;
+
+ //
+ // Initialize the rest of the device extension
+ //
+ pdoData->IsFDO = FALSE;
+ pdoData->Self = Pdo;
+
+ pdoData->ParentFdo = FdoData->Self;
+
+ pdoData->Started = FALSE; // irp_mn_start has yet to be received
+ pdoData->Attached = TRUE; // attached to the bus
+ //pdoData->Removed = FALSE; // no irp_mn_remove as of yet
+ pdoData->DebugLevel = FdoData->DebugLevel; // Copy the debug level
+
+ pdoData->DeviceState = PowerDeviceD0;
+ pdoData->SystemState = PowerSystemWorking;
+
+ //
+ // Add the pdo to serenum's list
+ //
+
+ ASSERT(FdoData->NewPDO == NULL);
+ ASSERT(FdoData->NewPdoData == NULL);
+ ASSERT(FdoData->NewNumPDOs == 0);
+
+ KeAcquireSpinLock(&FdoData->EnumerationLock, &oldIrql);
+
+ FdoData->NewPDO = Pdo;
+ FdoData->NewPdoData = pdoData;
+ FdoData->NewNumPDOs = 1;
+
+ FdoData->EnumFlags |= SERENUM_ENUMFLAG_DIRTY;
+
+ KeReleaseSpinLock(&FdoData->EnumerationLock, oldIrql);
+
+ Pdo->Flags &= ~DO_DEVICE_INITIALIZING;
+ Pdo->Flags |= DO_POWER_PAGABLE;
+}
+
+#if _MSC_VER >= 1200
+#pragma warning(pop)
+#endif
+
+#pragma warning(default:4127) // conditional expression is constant
+
+
diff --git a/serial/serenum/serenum.h b/serial/serenum/serenum.h
new file mode 100644
index 00000000..11bc6671
--- /dev/null
+++ b/serial/serenum/serenum.h
@@ -0,0 +1,626 @@
+/*++
+Copyright (c) 1997 Microsoft Corporation
+
+Module Name:
+
+ serenum.h
+
+Abstract:
+
+ This module contains the common private declarations for the serial port
+ enumerator.
+
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+
+Revision History:
+
+
+--*/
+
+#ifndef SERENUM_H
+#define SERENUM_H
+
+#define MAX_DEVNODE_NAME 256 // Total size of Device ID
+#define NAME_SERENUM "SERENUM\\"
+#define MAX_DEVNAME (MAX_DEVNODE_NAME + 1 - sizeof(NAME_SERENUM) )
+
+#define SERENUM_QDR_LOCK 0x00000001
+#define SERENUM_OPEN_LOCK 0x00000002
+#define SERENUM_POWER_LOCK 0x00000004
+#define SERENUM_STOP_LOCK 0x00000008
+#define SERENUM_EXPOSE_LOCK 0x00000010
+
+//#define SERENUM_COMPATIBLE_IDS L"SerialPort\\SerialDevice\0\0"
+//#define SERENUM_COMPATIBLE_IDS_LENGTH 25 // NB wide characters.
+
+#define SERENUM_INSTANCE_IDS L"0000"
+#define SERENUM_INSTANCE_IDS_LENGTH 5
+
+//
+// Default wait between line state changes in pnp enum protocol
+//
+
+#define SERENUM_DEFAULT_WAIT 2000000
+
+//
+// Timeout to read data from a serial port (in ms)
+//
+
+#define SERENUM_SERIAL_READ_TIME 240
+
+//#define SERENUM_INSTANCE_ID_BASE L"Serenum\\Inst_000"
+//#define SERENUM_INSTANCE_ID_BASE_LENGTH 12
+//#define SERENUM_INSTANCE_ID_BASE_PORT_INDEX 10
+
+#define SERENUM_PDO_NAME_BASE L"\\Serial\\"
+
+
+#define SERENUM_POOL_TAG (ULONG)'mneS'
+
+/*#undef ExAllocatePool
+#define ExAllocatePool(type, size) \
+ ExAllocatePoolWithTag(type, size, SERENUM_POOL_TAG)*/
+
+
+#pragma warning(error:4100) // Unreferenced formal parameter
+#pragma warning(error:4705) // Statement has no effect
+// disable warnings
+
+#if _MSC_VER >= 1200
+#pragma warning(push)
+#endif
+
+#pragma warning(disable:4201) // nameless struct/union
+#pragma warning(disable:28718)
+
+
+//
+// Debugging Output Levels
+//
+
+#define SER_DBG_STARTUP_SHUTDOWN_MASK 0x0000000F
+#define SER_DBG_SS_NOISE 0x00000001
+#define SER_DBG_SS_TRACE 0x00000002
+#define SER_DBG_SS_INFO 0x00000004
+#define SER_DBG_SS_ERROR 0x00000008
+
+#define SER_DBG_PNP_MASK 0x000000F0
+#define SER_DBG_PNP_NOISE 0x00000010
+#define SER_DBG_PNP_TRACE 0x00000020
+#define SER_DBG_PNP_INFO 0x00000040
+#define SER_DBG_PNP_ERROR 0x00000080
+#define SER_DBG_PNP_DUMP_PACKET 0x00000100
+
+
+#define SER_DEFAULT_DEBUG_OUTPUT_LEVEL 0
+
+#if DBG
+#define Serenum_KdPrint(_d_,_l_, _x_) \
+ if ((_d_)->DebugLevel & (_l_)) { \
+ DbgPrint ("SerEnum.SYS: "); \
+ DbgPrint _x_; \
+ }
+
+//#define Serenum_KdPrint_Cont(_d_,_l_, _x_) \
+ // if ((_d_)->DebugLevel & (_l_)) { \
+ // DbgPrint _x_; \
+ // }
+
+#define Serenum_KdPrint_Def(_l_, _x_) \
+ if (SER_DEFAULT_DEBUG_OUTPUT_LEVEL & (_l_)) { \
+ DbgPrint ("SerEnum.SYS: "); \
+ DbgPrint _x_; \
+ }
+
+#define TRAP() DbgBreakPoint()
+#define DbgRaiseIrql(_x_,_y_) KeRaiseIrql(_x_,_y_)
+#define DbgLowerIrql(_x_) KeLowerIrql(_x_)
+#else
+
+#define Serenum_KdPrint(_d_, _l_, _x_)
+#define Serenum_KdPrint_Cont(_d_, _l_, _x_)
+#define Serenum_KdPrint_Def(_l_, _x_)
+#define TRAP()
+#define DbgRaiseIrql(_x_,_y_)
+#define DbgLowerIrql(_x_)
+
+#endif
+
+#if !defined(MIN)
+#define MIN(_A_,_B_) (((_A_) < (_B_)) ? (_A_) : (_B_))
+#endif
+
+//
+// A common header for the device extensions of the PDOs and FDO
+//
+
+typedef struct _COMMON_DEVICE_DATA
+{
+ PDEVICE_OBJECT Self;
+ // A backpointer to the device object for which this is the extension
+
+ BOOLEAN IsFDO;
+ // Are we currently in a query power state?
+
+ BOOLEAN Removed;
+ // Has this device been removed? Should we fail any requests?
+
+ //
+ // Since we may hold onto irps for an arbitrarily time
+ // we need a remove lock so that our device does not get removed
+ // while an irp is being processed.
+
+ IO_REMOVE_LOCK RemoveLock;
+
+ ULONG DebugLevel;
+ SYSTEM_POWER_STATE SystemState;
+ DEVICE_POWER_STATE DeviceState;
+} COMMON_DEVICE_DATA, *PCOMMON_DEVICE_DATA;
+
+//
+// The device extension for the PDOs.
+// That is the serial ports of which this bus driver enumerates.
+// (IE there is a PDO for the 201 serial port).
+//
+
+typedef struct _PDO_DEVICE_DATA
+{
+ COMMON_DEVICE_DATA;
+
+ PDEVICE_OBJECT ParentFdo;
+ // A back pointer to the bus
+
+ UNICODE_STRING HardwareIDs;
+ // Either in the form of bus\device
+ // or *PNPXXXX - meaning root enumerated
+
+ UNICODE_STRING CompIDs;
+ // compatible ids to the hardware id
+
+ UNICODE_STRING DeviceIDs;
+ // Format: bus\device
+
+ //
+ // Text describing device
+ //
+
+ UNICODE_STRING DevDesc;
+
+ UNICODE_STRING SerialNo;
+
+ UNICODE_STRING PnPRev;
+
+ BOOLEAN Started;
+ BOOLEAN Attached;
+ // When a device (PDO) is found on a bus and presented as a device relation
+ // to the PlugPlay system, Attached is set to TRUE, and Removed to FALSE.
+ // When the bus driver determines that this PDO is no longer valid, because
+ // the device has gone away, it informs the PlugPlay system of the new
+ // device relastions, but it does not delete the device object at that time.
+ // The PDO is deleted only when the PlugPlay system has sent a remove IRP,
+ // and there is no longer a device on the bus.
+ //
+ // If the PlugPlay system sends a remove IRP then the Removed field is set
+ // to true, and all client (non PlugPlay system) accesses are failed.
+ // If the device is removed from the bus Attached is set to FALSE.
+ //
+ // During a query relations Irp Minor call, only the PDOs that are
+ // attached to the bus (and all that are attached to the bus) are returned
+ // (even if they have been removed).
+ //
+ // During a remove device Irp Minor call, if and only if, attached is set
+ // to FALSE, the PDO is deleted.
+ //
+} PDO_DEVICE_DATA, *PPDO_DEVICE_DATA;
+
+
+//
+// The device extension of the bus itself. From whence the PDO's are born.
+//
+
+typedef struct _FDO_DEVICE_DATA
+{
+ COMMON_DEVICE_DATA;
+
+ UCHAR PdoIndex;
+ // A number to keep track of the Pdo we're allocating.
+ // Increment every time we create a new PDO. It's ok that it wraps.
+
+ BOOLEAN Started;
+ // Are we on, have resources, etc?
+
+
+ BOOLEAN NumPDOs;
+ // The PDOs currently enumerated.
+
+ BOOLEAN NewNumPDOs;
+
+ BOOLEAN NewPDOForcedRemove;
+ BOOLEAN PDOForcedRemove;
+ // Was the last PDO removed by force using the internal ioctl?
+ // If so, when the next Query Device Relations is called, return only the
+ // currently enumerated pdos
+
+ //
+ // Our child PDO; we're hoping he will be a doctor someday
+ //
+
+ PDEVICE_OBJECT AttachedPDO;
+
+ //
+ // The new PDO after we've done enumeration
+ //
+
+ PDEVICE_OBJECT NewPDO;
+
+ PPDO_DEVICE_DATA PdoData;
+
+ PPDO_DEVICE_DATA NewPdoData;
+
+ PDEVICE_OBJECT UnderlyingPDO;
+ PDEVICE_OBJECT TopOfStack;
+ // the underlying bus PDO and the actual device object to which our
+ // FDO is attached
+
+
+ // the number of IRPs sent from the bus to the underlying device object
+
+ UNICODE_STRING DevClassAssocName;
+ // The name returned from IoRegisterDeviceClass Association,
+ // which is used as a handle for IoSetDev... and friends.
+
+ SYSTEM_POWER_STATE SystemWake;
+ DEVICE_POWER_STATE DeviceWake;
+
+ KSEMAPHORE CreateSemaphore;
+ PRKSEMAPHORE PCreateSemaphore;
+
+ //
+ // How many times we should skip enumerating devices
+ // after our stack is built
+ //
+
+ ULONG SkipEnumerations;
+
+ //
+ // Spinlock to protect enumerations
+ //
+
+ KSPIN_LOCK EnumerationLock;
+
+ //
+ // Flags containing state of enumeration
+ //
+
+ ULONG EnumFlags;
+
+ //
+ // Pointer to protocol thread object
+ //
+
+ PVOID ThreadObj;
+
+ //
+ // Pointer to work item to clean up thread ref
+ //
+
+ PIO_WORKITEM EnumWorkItem;
+
+
+ //
+ // Status of last thread create or STATUS_UNSUCCESSFUL if just not pending
+ //
+
+ NTSTATUS ProtocolThreadStatus;
+
+ //
+ // Count of running protocol threads
+ //
+
+ LONG ProtocolThreadCount;
+
+} FDO_DEVICE_DATA, *PFDO_DEVICE_DATA;
+
+typedef struct _SERENUM_RELEASE_CONTEXT {
+ PVOID ThreadObject;
+ PIO_WORKITEM WorkItem;
+} SERENUM_RELEASE_CONTEXT, *PSERENUM_RELEASE_CONTEXT;
+
+#if _MSC_VER >= 1200
+#pragma warning(pop)
+#endif
+
+#pragma warning(default:4201) // nameless struct/union
+
+
+#define SERENUM_ENUMFLAG_CLEAN 0x0L
+#define SERENUM_ENUMFLAG_PENDING 0x1L
+#define SERENUM_ENUMFLAG_DIRTY 0x2L
+#define SERENUM_ENUMFLAG_REMOVED 0x4L
+
+//
+// Free the buffer associated with a Unicode string
+// and re-init it to NULL
+//
+
+#define SerenumFreeUnicodeString(PStr) \
+{ \
+ if ((PStr)->Buffer != NULL) { \
+ ExFreePool((PStr)->Buffer); \
+ } \
+ RtlInitUnicodeString((PStr), NULL); \
+}
+
+//
+// Prototypes
+//
+
+_Dispatch_type_(IRP_MJ_CREATE)
+_Dispatch_type_(IRP_MJ_CLOSE)
+DRIVER_DISPATCH Serenum_CreateClose;
+
+_Dispatch_type_(IRP_MJ_DEVICE_CONTROL)
+DRIVER_DISPATCH Serenum_IoCtl;
+
+_Dispatch_type_(IRP_MJ_INTERNAL_DEVICE_CONTROL)
+DRIVER_DISPATCH Serenum_InternIoCtl;
+
+DRIVER_UNLOAD Serenum_DriverUnload;
+
+_Dispatch_type_(IRP_MJ_PNP)
+DRIVER_DISPATCH Serenum_PnP;
+
+_Dispatch_type_(IRP_MJ_POWER)
+DRIVER_DISPATCH Serenum_Power;
+
+DRIVER_ADD_DEVICE Serenum_AddDevice;
+
+NTSTATUS
+Serenum_PnPRemove (
+ PDEVICE_OBJECT Device,
+ PPDO_DEVICE_DATA PdoData
+ );
+
+NTSTATUS
+Serenum_FDO_PnP (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpStack,
+ IN PFDO_DEVICE_DATA DeviceData
+ );
+
+NTSTATUS
+Serenum_PDO_PnP (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpStack,
+ IN PPDO_DEVICE_DATA DeviceData
+ );
+
+_Dispatch_type_(IRP_MJ_OTHER)
+DRIVER_DISPATCH Serenum_DispatchPassThrough;
+
+NTSTATUS
+Serenum_Generic_FireAndRelease_RemoveLock(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+
+NTSTATUS
+Serenum_ReenumerateDevices(
+ IN PIRP Irp,
+ IN PFDO_DEVICE_DATA DeviceData,
+ IN PBOOLEAN PSameDevice
+ );
+
+IO_COMPLETION_ROUTINE
+Serenum_EnumComplete;
+
+IO_COMPLETION_ROUTINE
+Serenum_CompletionRoutine;
+
+IO_COMPLETION_ROUTINE
+SerenumStartDeviceCompletion;
+
+IO_WORKITEM_ROUTINE_EX
+SerenumStartDeviceWorker;
+
+NTSTATUS
+Serenum_DispatchPassThroughWithoutAcquire(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+NTSTATUS
+Serenum_InitMultiString(PFDO_DEVICE_DATA FdoData, PUNICODE_STRING MultiString,
+ ...);
+
+int
+Serenum_GetDevOtherID(
+ _In_reads_(len) PCHAR input,
+ int len,
+ _Out_writes_(MAX_DEVNODE_NAME) LPSTR output
+ );
+
+NTSTATUS
+Serenum_GetDevPnPRev(
+ PFDO_DEVICE_DATA FdoData,
+ _In_reads_(len) PCHAR input,
+ int len,
+ _Out_writes_(MAX_DEVNODE_NAME) LPSTR output,
+ _Inout_ int *start
+ );
+
+void Serenum_GetDevName(
+ _In_reads_(len) PCHAR input,
+ int len,
+ _Out_writes_(MAX_DEVNAME) PCHAR output,
+ _Inout_ int *start
+ );
+
+void Serenum_GetDevSerialNo(
+ _In_reads_(len) PCHAR input,
+ int len,
+ _Out_writes_(MAX_DEVNODE_NAME) LPSTR output,
+ _Inout_ int *start
+ );
+
+void Serenum_GetDevClass(
+ _In_reads_(len) PCHAR input,
+ int len,
+ _Out_writes_(MAX_DEVNODE_NAME) LPSTR output,
+ _Inout_ int *start
+ );
+
+void Serenum_GetDevCompId(
+ _In_reads_(len) PCHAR input,
+ int len,
+ _Out_writes_(MAX_DEVNODE_NAME) LPSTR output,
+ _Inout_ int *start
+ );
+
+void Serenum_GetDevDesc(
+ _In_reads_(len) PCHAR input,
+ int len,
+ _Out_writes_(MAX_DEVNODE_NAME) LPSTR output,
+ _Inout_ int *start
+ );
+
+NTSTATUS
+Serenum_ParseData( PFDO_DEVICE_DATA FdoData,
+ _In_reads_(BufferLen) PCHAR ReadBuffer, ULONG BufferLen,
+ PUNICODE_STRING hardwareIDs, PUNICODE_STRING compIDs,
+ PUNICODE_STRING deviceIDs, PUNICODE_STRING PDeviceDesc,
+ PUNICODE_STRING serialNo, PUNICODE_STRING pnpRev
+ );
+
+NTSTATUS
+Serenum_ReadSerialPort(
+ _Out_writes_(Buflen) OUT PCHAR PReadBuffer, IN USHORT Buflen,
+ IN ULONG Timeout, OUT PUSHORT nActual,
+ OUT PIO_STATUS_BLOCK IoStatusBlock,
+ IN const PFDO_DEVICE_DATA FdoData);
+
+NTSTATUS
+Serenum_Wait (
+ IN PKTIMER Timer,
+ IN LARGE_INTEGER DueTime );
+
+#define Serenum_IoSyncIoctl(Ioctl, Internal, PDevObj, PEvent) \
+ Serenum_IoSyncIoctlEx((Ioctl), (Internal), (PDevObj), (PEvent), \
+ NULL, 0, NULL, 0)
+
+NTSTATUS
+Serenum_IoSyncIoctlEx(ULONG Ioctl, BOOLEAN Internal, PDEVICE_OBJECT PDevObj,
+ PKEVENT PEvent, PVOID PInBuffer, ULONG InBufferLen,
+ PVOID POutBuffer, ULONG OutBufferLen);
+
+NTSTATUS
+Serenum_IoSyncReqWithIrp(
+ PIRP Irp,
+ UCHAR MajorFunction,
+ PKEVENT event,
+ PDEVICE_OBJECT devobj );
+
+NTSTATUS
+Serenum_IoSyncReq(
+ PDEVICE_OBJECT Target,
+ IN PIRP Irp,
+ PKEVENT event
+ );
+
+NTSTATUS
+Serenum_CopyUniString (
+ PUNICODE_STRING source,
+ PUNICODE_STRING dest);
+
+void
+Serenum_FixptToAscii(
+ int n,
+ _Out_writes_(MAX_DEVNODE_NAME) LPSTR output);
+
+int
+Serenum_HToI(char c);
+
+int
+Serenum_SzCopy (
+ _In_ LPSTR source,
+ _Out_writes_(len) PCHAR dest,
+ int len
+ );
+
+void
+Serenum_PDO_EnumMarkMissing(
+ PFDO_DEVICE_DATA FdoData,
+ PPDO_DEVICE_DATA PdoData);
+
+int
+Serenum_StrLen (
+ _In_ LPSTR string);
+
+
+NTSTATUS
+Serenum_GetRegistryKeyValue (
+ _In_ HANDLE Handle,
+ _In_reads_bytes_(KeyNameStringLength) PWCHAR KeyNameString,
+ _In_ ULONG KeyNameStringLength,
+ _Out_writes_bytes_to_(DataLength, *ActualLength) PVOID Data,
+ _In_ ULONG DataLength,
+ _Out_ PULONG ActualLength
+ );
+
+void
+Serenum_InitPDO (
+ _In_ __drv_aliasesMem PDEVICE_OBJECT pdoData,
+ PFDO_DEVICE_DATA fdoData
+ );
+
+void
+SerenumScanOtherIdForMouse(
+ _In_reads_(BufLen) PCHAR PBuffer,
+ _In_ ULONG BufLen,
+ _Outptr_result_buffer_maybenull_(*PmouseIdLen) PCHAR *PpMouseId,
+ _Out_ ULONG *PmouseIdLen
+ );
+
+IO_COMPLETION_ROUTINE
+SerenumSyncCompletion;
+
+NTSTATUS
+SerenumDoEnumProtocol(
+ PFDO_DEVICE_DATA PFdoData,
+ _Outptr_result_buffer_(*PNBytes) PUCHAR *PpBuf,
+ PUSHORT PNBytes,
+ PBOOLEAN PDSRMissing);
+
+BOOLEAN
+SerenumValidateID(IN PUNICODE_STRING PId);
+
+BOOLEAN
+SerenumCheckForLegacyDevice(IN PFDO_DEVICE_DATA PFdoData,
+ _In_reads_(BufferLen) IN PCHAR PReadBuf,
+ IN ULONG BufferLen,
+ IN OUT PUNICODE_STRING PHardwareIDs,
+ IN OUT PUNICODE_STRING PCompIDs,
+ IN OUT PUNICODE_STRING PDeviceIDs);
+
+NTSTATUS
+SerenumStartProtocolThread(IN PFDO_DEVICE_DATA PFdoData);
+
+VOID
+SerenumReleaseThreadReference(IN PDEVICE_OBJECT PDevObj, IN PVOID PContext);
+
+VOID
+SerenumWaitForEnumThreadTerminate(IN PFDO_DEVICE_DATA PFdoData);
+
+
+#endif // ndef SERENUM_H
+
+
diff --git a/serial/serenum/serenum.rc b/serial/serenum/serenum.rc
new file mode 100644
index 00000000..21694531
--- /dev/null
+++ b/serial/serenum/serenum.rc
@@ -0,0 +1,16 @@
+//
+// Copyright (C) Microsoft. All rights reserved.
+//
+
+#include <windows.h>
+
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_DLL
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "Serial Port Enumerator"
+#define VER_INTERNALNAME_STR "serenum.sys"
+#define VER_ORIGINALFILENAME_STR "serenum.sys"
+
+#include "common.ver"
+
diff --git a/serial/serenum/serenum.sln b/serial/serenum/serenum.sln
new file mode 100644
index 00000000..3e0480c9
--- /dev/null
+++ b/serial/serenum/serenum.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}") = "SerEnum_sample", "SerEnum_sample.vcxproj", "{E52D8BEE-C6BE-4357-902C-C6DB9C0EF34F}"
+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
+ {E52D8BEE-C6BE-4357-902C-C6DB9C0EF34F}.Debug|Win32.ActiveCfg = Debug|Win32
+ {E52D8BEE-C6BE-4357-902C-C6DB9C0EF34F}.Debug|Win32.Build.0 = Debug|Win32
+ {E52D8BEE-C6BE-4357-902C-C6DB9C0EF34F}.Release|Win32.ActiveCfg = Release|Win32
+ {E52D8BEE-C6BE-4357-902C-C6DB9C0EF34F}.Release|Win32.Build.0 = Release|Win32
+ {E52D8BEE-C6BE-4357-902C-C6DB9C0EF34F}.Debug|x64.ActiveCfg = Debug|x64
+ {E52D8BEE-C6BE-4357-902C-C6DB9C0EF34F}.Debug|x64.Build.0 = Debug|x64
+ {E52D8BEE-C6BE-4357-902C-C6DB9C0EF34F}.Release|x64.ActiveCfg = Release|x64
+ {E52D8BEE-C6BE-4357-902C-C6DB9C0EF34F}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/serial/serenum/string.c b/serial/serenum/string.c
new file mode 100644
index 00000000..bf54ca8e
--- /dev/null
+++ b/serial/serenum/string.c
@@ -0,0 +1,1029 @@
+/*++
+Copyright (c) 1998 Microsoft Corporation
+
+Module Name:
+
+ STRING.C
+
+Abstract:
+
+ This module contains the functions used to parse the PNP COM ID
+ and save it in the appropriate UNICODE STRINGS. The main function
+ that is called is Serenum_ParseData. All other functions are called
+ by this main function.
+
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+
+
+--*/
+
+#include "pch.h"
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text (PAGE, Serenum_ParseData)
+
+// Called by ParseData:
+#pragma alloc_text (PAGE, Serenum_GetDevDesc)
+#pragma alloc_text (PAGE, Serenum_GetDevCompId)
+#pragma alloc_text (PAGE, Serenum_GetDevClass)
+#pragma alloc_text (PAGE, Serenum_GetDevSerialNo)
+#pragma alloc_text (PAGE, Serenum_GetDevName)
+#pragma alloc_text (PAGE, Serenum_GetDevPnPRev)
+#pragma alloc_text (PAGE, Serenum_GetDevOtherID)
+
+#pragma alloc_text (PAGE, Serenum_InitMultiString)
+#pragma alloc_text (PAGE, Serenum_SzCopy)
+#pragma alloc_text (PAGE, Serenum_StrLen)
+
+// Called by the above functions:
+#pragma alloc_text (PAGE, Serenum_FixptToAscii)
+#pragma alloc_text (PAGE, Serenum_HToI)
+
+#endif
+
+NTSTATUS
+Serenum_ParseData( PFDO_DEVICE_DATA FdoData,
+ _In_reads_(BufferLen) PCHAR ReadBuffer, ULONG BufferLen,
+ PUNICODE_STRING hardwareIDs, PUNICODE_STRING compIDs,
+ PUNICODE_STRING deviceIDs, PUNICODE_STRING PDeviceDesc,
+ PUNICODE_STRING serialNo, PUNICODE_STRING pnpRev
+ )
+/*++
+
+Routine Description:
+ Parses the PNP COM ID out of the buffer which is passed as the
+ first parameter and then saves the appropriate IDs as
+ UNICODE_STRINGS in the other passed parameters.
+
+Return value:
+ NTSTATUS
+
+--*/
+
+{
+ LPSTR pOtherId = NULL;
+ LPSTR pPnpRev = NULL;
+ LPSTR pDevNodeName = NULL;
+ LPSTR pSerNo = NULL;
+ LPSTR pClass = NULL;
+ LPSTR pCompIdStar = NULL;
+ LPSTR pDesc = NULL;
+ PCHAR pStrBuffer = NULL;
+
+ NTSTATUS status = STATUS_SUCCESS;
+
+ LPSTR pDevName = NULL;
+ LPSTR pCompId = NULL;
+
+ int start;
+
+ BOOLEAN isMouse = FALSE;
+ PCHAR pMouseID = NULL;
+ ULONG mouseIDLen;
+
+ UNREFERENCED_PARAMETER(BufferLen);
+ PAGED_CODE();
+
+ //
+ // Allocate the string buffers
+ //
+
+ pStrBuffer = ExAllocatePoolWithTag(PagedPool, MAX_DEVNODE_NAME * 7 + 1,SERENUM_POOL_TAG);
+
+ if (pStrBuffer == NULL) {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ goto DoneParsingErr;
+ } else {
+ PCHAR pCurBuffer = pStrBuffer;
+
+ pOtherId = pCurBuffer;
+ *pOtherId = '\0';
+ pCurBuffer += MAX_DEVNODE_NAME;
+
+ pPnpRev = pCurBuffer;
+ *pPnpRev = '\0';
+ pCurBuffer += MAX_DEVNODE_NAME;
+
+ pDevNodeName = pCurBuffer;
+ *pDevNodeName = '\0';
+ pCurBuffer += MAX_DEVNODE_NAME;
+
+ pSerNo = pCurBuffer;
+ *pSerNo = '\0';
+ pCurBuffer += MAX_DEVNODE_NAME;
+
+ pClass = pCurBuffer;
+ *pClass = '\0';
+ pCurBuffer += MAX_DEVNODE_NAME;
+
+ pCompIdStar = pCurBuffer;
+ pCompId = pCompIdStar + 1;
+ *pCompIdStar = '\0';
+ pCurBuffer += MAX_DEVNODE_NAME + 1;
+
+ pDesc = pCurBuffer;
+ *pDesc = '\0';
+ pCurBuffer += MAX_DEVNODE_NAME;
+ }
+
+ start = Serenum_SzCopy ( NAME_SERENUM, pDevNodeName, MAX_DEVNODE_NAME);
+ pDevName = pDevNodeName + start;
+
+ start = 0;
+
+ RtlInitUnicodeString(hardwareIDs, NULL);
+ RtlInitUnicodeString(compIDs, NULL);
+ RtlInitUnicodeString(deviceIDs, NULL);
+ RtlInitUnicodeString(pnpRev, NULL);
+ RtlInitUnicodeString(serialNo, NULL);
+
+
+ //
+ // OtherID
+ //
+
+ if(ReadBuffer!=NULL&&BufferLen>=MAX_DEVNODE_NAME){
+ start = Serenum_GetDevOtherID(ReadBuffer, BufferLen, pOtherId);
+ }
+
+ if (start > 16||start==0) {
+ Serenum_KdPrint(FdoData, SER_DBG_SS_ERROR,
+ ("Other ID string too long\n"));
+ status = STATUS_UNSUCCESSFUL;
+ goto DoneParsingErr;
+ }
+
+ Serenum_KdPrint(FdoData, SER_DBG_SS_TRACE, ("Other ID: %s\n", pOtherId));
+
+ //
+ // See if this is a mouse
+ //
+
+ SerenumScanOtherIdForMouse(ReadBuffer, BufferLen, &pMouseID, &mouseIDLen );
+
+ if (pMouseID != NULL && (*pMouseID == 'M' || *pMouseID == 'B')) {
+ isMouse = TRUE;
+ }
+
+ //
+ // PNP revision number
+ //
+
+ status = Serenum_GetDevPnPRev(FdoData, ReadBuffer, BufferLen, pPnpRev, &start);
+
+ if (!NT_SUCCESS(status)) {
+ Serenum_KdPrint(FdoData, SER_DBG_SS_ERROR, ("PNP ID string bad\n"));
+ goto DoneParsingErr;
+ }
+
+ Serenum_KdPrint(FdoData, SER_DBG_SS_TRACE, ("PNP Revision: %s\n", pPnpRev));
+
+ //
+ // PNP device node name
+ // EISA ID followed by Product ID
+ //
+
+ Serenum_GetDevName(ReadBuffer, BufferLen, pDevName, &start);
+ Serenum_KdPrint(FdoData, SER_DBG_SS_TRACE, ("Device Node name: %s\n",
+ pDevNodeName));
+
+ //
+ // Device serial number
+ //
+ if(ReadBuffer!=NULL){
+ Serenum_GetDevSerialNo(ReadBuffer, BufferLen, pSerNo, &start);
+ }
+
+ if (Serenum_StrLen(pSerNo)) {
+ //
+ // This field exists - Make sure it is correct length.
+ //
+
+ if (Serenum_StrLen(pSerNo) != 8) {
+ Serenum_KdPrint(FdoData, SER_DBG_SS_ERROR, ("Serial number wrong"
+ " length\n"));
+ *pSerNo = '\0';
+ status = STATUS_UNSUCCESSFUL;
+ goto DoneParsingErr;
+ }
+ }
+
+ Serenum_KdPrint(FdoData, SER_DBG_SS_TRACE, ("Serial Number: %s\n", pSerNo));
+
+ //
+ // PNP class identifier
+ //
+ if(ReadBuffer!=NULL){
+ Serenum_GetDevClass(ReadBuffer, BufferLen, pClass, &start);
+ }
+
+ if (Serenum_StrLen(pClass) > 32) {
+ Serenum_KdPrint(FdoData, SER_DBG_SS_ERROR, ("Class ID string too long\n"
+ ));
+ status = STATUS_UNSUCCESSFUL;
+ goto DoneParsingErr;
+ }
+
+ Serenum_KdPrint(FdoData, SER_DBG_SS_TRACE, ("Class: %s\n", pClass));
+
+ if (_stricmp(pClass, "MOUSE") == 0) {
+ RtlStringCchCopyA(pClass, MAX_DEVNODE_NAME, "SERIAL_MOUSE");
+ }
+
+ //
+ // Compatible device ID
+ //
+ *pCompIdStar = '*';
+ if(ReadBuffer!=NULL&&pCompId!=NULL){
+ Serenum_GetDevCompId(ReadBuffer, BufferLen, pCompId, &start);
+ }
+
+ if (Serenum_StrLen(pCompId) > 40) {
+ Serenum_KdPrint(FdoData, SER_DBG_SS_ERROR, ("Compatible driver ID"
+ " string too long\n"));
+ status = STATUS_UNSUCCESSFUL;
+ goto DoneParsingErr;
+ }
+
+ Serenum_KdPrint(FdoData, SER_DBG_SS_TRACE, ("Compatible driver ID: %s\n",
+ pCompId));
+
+ //
+ // End-user legible Product Description
+ //
+ if(ReadBuffer!=NULL){
+ Serenum_GetDevDesc (ReadBuffer, BufferLen, pDesc, &start);
+ }
+
+ if (Serenum_StrLen(pDesc) > 40) {
+ Serenum_KdPrint(FdoData, SER_DBG_SS_ERROR, ("Device Description too"
+ " long\n"));
+ status = STATUS_UNSUCCESSFUL;
+ goto DoneParsingErr;
+ }
+ Serenum_KdPrint(FdoData, SER_DBG_SS_TRACE, ("Device Description: %s\n",
+ pDesc));
+
+ DoneParsingErr:
+ if (pStrBuffer != NULL) {
+ //
+ // send back the good bits so that routine knows what driver to load
+ //
+
+ Serenum_InitMultiString (FdoData, hardwareIDs, pDevNodeName, pDevName,
+ NULL);
+
+ if (Serenum_StrLen(pCompId) > 0) {
+ if (!isMouse) {
+ Serenum_InitMultiString(FdoData, compIDs, pCompIdStar, pClass,
+ NULL);
+ } else {
+ Serenum_InitMultiString(FdoData, compIDs, pCompIdStar, pClass,
+ "SERIAL_MOUSE", NULL);
+ }
+ } else {
+ if (isMouse) {
+ Serenum_InitMultiString(FdoData, compIDs, "SERIAL_MOUSE", NULL);
+ }
+ }
+
+ Serenum_InitMultiString(FdoData, deviceIDs, pDevNodeName, NULL);
+
+ Serenum_InitMultiString(FdoData, PDeviceDesc, pDesc, NULL);
+
+ if (Serenum_StrLen(pSerNo)) {
+ Serenum_InitMultiString(FdoData, serialNo, pSerNo, NULL);
+ }
+ if (Serenum_StrLen(pPnpRev)) {
+ Serenum_InitMultiString(FdoData, pnpRev, pPnpRev, NULL);
+ }
+
+ ExFreePoolWithTag(pStrBuffer,SERENUM_POOL_TAG);
+ }
+
+ return status;
+}
+
+NTSTATUS
+Serenum_InitMultiString(PFDO_DEVICE_DATA FdoData, PUNICODE_STRING MultiString,
+ ...)
+/*++
+
+ This routine will take a null terminated list of ascii strings and combine
+ them together to generate a unicode multi-string block
+
+Arguments:
+
+ MultiString - a unicode structure in which a multi-string will be built
+ ... - a null terminated list of narrow strings which will be
+ combined together. This list must contain at least a
+ trailing NULL
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+{
+ ANSI_STRING ansiString;
+ NTSTATUS status;
+ PCSTR rawString;
+ ULONG multiLength = 0;
+ UNICODE_STRING unicodeString;
+ va_list ap;
+ ULONG i;
+
+ PAGED_CODE();
+
+#if !DBG
+ UNREFERENCED_PARAMETER(FdoData);
+#endif
+
+
+ Serenum_KdPrint(FdoData, SER_DBG_SS_TRACE,
+ ("Entering Serenum_InitMultiString\n"));
+
+ va_start(ap,MultiString);
+
+ //
+ // Make sure that we won't leak memory
+ //
+
+ ASSERT(MultiString->Buffer == NULL);
+
+ rawString = va_arg(ap, PCSTR);
+
+ while (rawString != NULL) {
+ RtlInitAnsiString(&ansiString, rawString);
+ multiLength += RtlAnsiStringToUnicodeSize(&(ansiString));
+ rawString = va_arg(ap, PCSTR);
+ }
+
+ va_end( ap );
+
+ if (multiLength == 0) {
+ //
+ // Done
+ //
+ RtlInitUnicodeString(MultiString, NULL);
+ Serenum_KdPrint(FdoData, SER_DBG_SS_TRACE,
+ ("Leaving Serenum_InitMultiString (1)\n"));
+
+ return STATUS_SUCCESS;
+ }
+
+ //
+ // We need an extra null
+ //
+ multiLength += sizeof(WCHAR);
+
+ MultiString->MaximumLength = (USHORT)multiLength;
+ MultiString->Buffer = ExAllocatePoolWithTag(PagedPool, multiLength,SERENUM_POOL_TAG);
+ MultiString->Length = 0;
+
+ if (MultiString->Buffer == NULL) {
+ Serenum_KdPrint(FdoData, SER_DBG_SS_TRACE,
+ ("Leaving Serenum_InitMultiString (2)\n"));
+
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ Serenum_KdPrint(FdoData, SER_DBG_SS_TRACE,
+ ("Allocated %lu bytes for buffer\n", multiLength));
+
+#if DBG
+ RtlFillMemory(MultiString->Buffer, multiLength, 0xff);
+#endif
+
+ unicodeString.Buffer = MultiString->Buffer;
+ unicodeString.MaximumLength = (USHORT) multiLength;
+ unicodeString.Length = 0;
+
+ va_start(ap, MultiString);
+ rawString = va_arg(ap, PCSTR);
+
+ while (rawString != NULL) {
+
+ RtlInitAnsiString(&ansiString,rawString);
+ status = RtlAnsiStringToUnicodeString(&unicodeString, &ansiString, FALSE);
+ if (!NT_SUCCESS(status)) {
+ Serenum_KdPrint(FdoData, SER_DBG_SS_ERROR, ("Serenum_InitMultiString: Call to RtlAnsiStringToUnicodeString Failed.\n"));
+ return status;
+ }
+
+ //
+ // We don't allocate memory, so if something goes wrong here,
+ // its the function that's at fault
+ //
+ ASSERT(NT_SUCCESS(status));
+
+ //
+ // Check for any commas and replace them with NULLs
+ //
+
+ ASSERT(unicodeString.Length % sizeof(WCHAR) == 0);
+
+ for (i = 0; i < (unicodeString.Length / sizeof(WCHAR)); i++) {
+ if (unicodeString.Buffer[i] == L'\x2C' ||
+ unicodeString.Buffer[i] == L'\x0C' ) {
+ unicodeString.Buffer[i] = L'\0';
+ }
+ }
+
+ Serenum_KdPrint(FdoData, SER_DBG_SS_TRACE, ("unicode buffer: %ws\n",
+ unicodeString.Buffer));
+
+ //
+ // Move the buffers along
+ //
+ unicodeString.Buffer += ((unicodeString.Length / sizeof(WCHAR)) + 1);
+ unicodeString.MaximumLength -= (unicodeString.Length + sizeof(WCHAR));
+ unicodeString.Length = 0;
+
+ //
+ // Next
+ //
+
+ rawString = va_arg(ap, PCSTR);
+ } // while
+
+ va_end(ap);
+
+ ASSERT(unicodeString.MaximumLength == sizeof(WCHAR));
+
+ //
+ // Stick the final null there
+ //
+
+ Serenum_KdPrint(FdoData, SER_DBG_SS_TRACE, ("unicode buffer last addr: "
+ "%p\n", unicodeString.Buffer));
+
+ unicodeString.Buffer[0] = L'\0';
+
+ //
+ // Include the nulls in the length of the string
+ //
+
+ MultiString->Length = (USHORT)multiLength;
+ MultiString->MaximumLength = MultiString->Length;
+
+ Serenum_KdPrint(FdoData, SER_DBG_SS_TRACE,
+ ("Leaving Serenum_InitMultiString (3)\n"));
+
+ return STATUS_SUCCESS;
+}
+
+int
+Serenum_StrLen (
+ _In_ LPSTR string)
+// Measures the length of a string
+{
+ int i;
+ PAGED_CODE();
+
+ if (string == NULL) {
+ return 0;
+ }
+ for (i=0; string[i] != '\0'; i++) {
+ }
+ return i;
+}
+
+int
+Serenum_SzCopy (
+ _In_ LPSTR source,
+ _Out_writes_(len) PCHAR dest,
+ int len
+ )
+// Copies a string
+// Assumes the buffer is already allocated to be copied into
+{
+ int i;
+
+ PAGED_CODE();
+
+ ASSERT (source);
+ ASSERT (dest);
+
+ for (i=0; source[i] != '\0' && i < len; i++) {
+ *dest++ = source[i];
+ }
+ return i;
+}
+
+//
+// String extraction functions:
+//
+int
+Serenum_GetDevOtherID(
+ _In_reads_(len) PCHAR input,
+ int len,
+ _Out_writes_(MAX_DEVNODE_NAME)LPSTR output
+ )
+{
+ int tail;
+ CHAR c;
+
+ PAGED_CODE();
+
+ tail = 0;
+ c = input[tail++];
+ while((tail < 17 ) && (c != '(') && (c != '(' - 0x20) && tail < len ) {
+ *output++ = c;
+#pragma warning(suppress: 6385)
+ c = input[tail++];
+ }
+ *output = '\0';
+ return(tail-1);
+}
+
+
+/****************************************************************************
+ *
+ *
+ ***************************************************************************/
+int
+Serenum_HToI(char c)
+{
+ PAGED_CODE();
+
+ if('0' <= c && c <= '9')
+ return(c - '0');
+
+ if('A' <= c && c <= 'F')
+ return(c - 'A' + 10);
+
+ if('a' <= c && c <= 'f')
+ return(c - 'a' + 10);
+
+ return(-1);
+}
+
+void
+Serenum_FixptToAscii(
+ int n,
+ _Out_writes_(MAX_DEVNODE_NAME) LPSTR output)
+/****************************************************************************
+ *
+ *
+ ***************************************************************************/
+{
+ int tmp;
+
+ PAGED_CODE();
+
+ tmp = n / 100;
+
+ if(tmp >= 10)
+ *output++ = (CHAR)('0' + (tmp / 10));
+
+ *output++ = (CHAR)('0' + (tmp % 10));
+ *output++ = '.';
+
+ tmp = n % 100;
+
+ *output++ = (CHAR)('0' + (tmp / 10));
+ *output++ = (CHAR)('0' + (tmp % 10));
+
+ *output = '\0';
+}
+
+/****************************************************************************
+ *
+ *
+ ***************************************************************************/
+NTSTATUS
+Serenum_GetDevPnPRev(
+ PFDO_DEVICE_DATA FdoData,
+ _In_reads_(len) PCHAR input,
+ int len,
+ _Out_writes_(MAX_DEVNODE_NAME) LPSTR output,
+ _Inout_ int *start
+ )
+{
+ int tail;
+ int i;
+ char delta;
+ char c, begin_PnP, end_PnP_pos;
+ int sum, chk_sum, msd, lsd;
+
+ UNREFERENCED_PARAMETER(FdoData);
+ PAGED_CODE();
+
+ if (output == NULL || input == NULL) {
+ Serenum_KdPrint(FdoData, SER_DBG_SS_ERROR,
+ ("GetDevPnPRev Failed, NULL pointer!\n"));
+ return STATUS_UNSUCCESSFUL;
+ }
+ __analysis_assume(len>=MAX_DEVNODE_NAME);
+ *output = '\0';
+
+ tail = *start;
+
+ if ( tail >= len ) {
+ Serenum_KdPrint(FdoData, SER_DBG_SS_ERROR,
+ ("GetDevPnPRev Failed, insufficient input buffer\n"));
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ if (input[tail] == 0) {
+ Serenum_KdPrint(FdoData, SER_DBG_SS_ERROR,
+ ("GetDevPnPRev Failed, input buffer empty!\n"));
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ c = input[tail++];
+
+ while ((tail < 256) && (c != '(') && (c != '(' - 0x20) && tail < len) {
+ c = input[tail++];
+ }
+
+ if (c != '(' && c != '(' - 0x20) {
+ Serenum_KdPrint(FdoData, SER_DBG_SS_ERROR,
+ ("GetDevPnPRev Failed, no Begin PnP char!\n"));
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ begin_PnP = c;
+ delta = '(' - begin_PnP;
+
+ if ( tail+9 >= len ) {
+ Serenum_KdPrint(FdoData, SER_DBG_SS_ERROR,
+ ("GetDevPnPRev Failed, insufficient input buffer\n"));
+ return STATUS_UNSUCCESSFUL;
+ }
+
+#pragma warning(suppress: 6385)
+ if ( input[tail + 9] != ')' - delta) {
+
+ //
+ // compute checksum
+ //
+
+ sum = c;
+ i = tail;
+ while ( (i < 256) && (c != ( ')' - delta)) && i < len ) {
+ c = input[i++];
+ sum += c;
+ }
+
+ if ( i < 3 || i-2 >= len) {
+ Serenum_KdPrint(FdoData, SER_DBG_SS_ERROR,
+ ("GetDevPnPRev Failed, insufficient input buffer\n"));
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ msd = input[i-3];
+ lsd = input[i-2];
+
+ sum -= msd;
+ sum -= lsd;
+
+ msd += delta;
+ lsd += delta;
+
+ Serenum_KdPrint(FdoData, SER_DBG_SS_TRACE,
+ ("checksum from device (chars) = %c%c\n", (char)msd,
+ (char)lsd));
+
+ msd = Serenum_HToI((char)msd);
+ if (msd < 0) {
+ Serenum_KdPrint(FdoData, SER_DBG_SS_ERROR,
+ ("Bad msd checksum digit\n"));
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ lsd = Serenum_HToI((char)lsd);
+ if (lsd < 0) {
+ Serenum_KdPrint(FdoData, SER_DBG_SS_ERROR,
+ ("Bad lsd checksum digit\n"));
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ chk_sum = (msd << 4) + lsd;
+
+ sum &= 0xff;
+
+ Serenum_KdPrint(FdoData, SER_DBG_SS_TRACE,
+ ("checksum read from device = %0x\n", chk_sum));
+ Serenum_KdPrint(FdoData, SER_DBG_SS_TRACE,
+ ("Computed checksum = %0x\n", sum));
+
+ end_PnP_pos = (char)i;
+
+ if ( c - begin_PnP != ')' - '(' ) {
+ Serenum_KdPrint(FdoData, SER_DBG_SS_ERROR,
+ ("GetDevPnPRev Failed,BeginPnP didn't match "
+ "EndPnP\n"));
+ Serenum_KdPrint(FdoData, SER_DBG_SS_ERROR,
+ ("begin_PnP = %02x end_PnP = %02x\n", begin_PnP, c));
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ //
+ // check the checksum
+ //
+
+ if (chk_sum != sum) {
+ Serenum_KdPrint(FdoData, SER_DBG_SS_ERROR,
+ ("checksum Failed! Continuing...\n"));
+ // return STATUS_UNSUCCESSFUL; // Commented out in Memphis
+ }
+
+ i = end_PnP_pos;
+
+ if ( i < 3 || i-2 >= len) {
+ Serenum_KdPrint(FdoData, SER_DBG_SS_ERROR,
+ ("GetDevPnPRev Failed, insufficient input buffer\n"));
+ return STATUS_UNSUCCESSFUL;
+ }
+ input[i-3] = ')' - delta; // trash the checksum
+ input[i-2] = '\0'; // since we are done with it
+ }
+
+ if ( tail+1 >= len ) {
+ Serenum_KdPrint(FdoData, SER_DBG_SS_ERROR,
+ ("GetDevPnPRev Failed, insufficient input buffer\n"));
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ if (input[tail] > 0x3f ||
+ input[tail+1] > 0x3f) {
+
+ Serenum_KdPrint(FdoData, SER_DBG_SS_TRACE, ("Bad PnP Rev digits\n"));
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ i = (input[tail++] & 0x3f) << 6;
+ i |= (input[tail++]) & 0x3f;
+
+ Serenum_FixptToAscii(i, output);
+
+ i = tail;
+
+ //
+ // get ride of Mouse'output 0x20 bias in the string
+ //
+ while ( (i < 256) && delta && i < len ) {
+ input[i] += delta;
+ c = input[i++];
+ if ( c == ')' ) {
+ delta = 0; // indicate we are done
+ }
+ }
+
+ *start = tail;
+
+ return STATUS_SUCCESS;
+}
+
+/****************************************************************************
+ *
+ *
+ ***************************************************************************/
+void Serenum_GetDevName(
+ _In_reads_(len) PCHAR input,
+ int len,
+ _Out_writes_(MAX_DEVNAME) PCHAR output,
+ _Inout_ int *start
+ )
+{
+
+ int tail;
+ char c;
+
+ PAGED_CODE();
+
+ if(output == NULL || input == NULL)
+ return;
+
+ tail = *start;
+
+ // EISA ID
+ if ( tail >= len ) { *start = tail; goto Exit; }
+ *output++ = input[tail++];
+ if ( tail >= len ) { *start = tail; goto Exit; }
+ *output++ = input[tail++];
+ if ( tail >= len ) { *start = tail; goto Exit; }
+ *output++ = input[tail++];
+
+ // Product ID
+
+ if ( tail >= len ) { *start = tail; goto Exit; }
+ c = input[tail++];
+ if(Serenum_HToI(c) >= 0)
+ *output++ = c;
+
+ if ( tail >= len ) { *start = tail; goto Exit; }
+ c = input[tail++];
+ if(Serenum_HToI(c) >= 0)
+ *output++ = c;
+
+ if ( tail >= len ) { *start = tail; goto Exit; }
+ c = input[tail++];
+ if(Serenum_HToI(c) >= 0)
+ *output++ = c;
+
+ if ( tail >= len ) { *start = tail; goto Exit; }
+ c = input[tail++];
+ if(Serenum_HToI(c) >= 0)
+ *output++ = c;
+
+ Exit:
+ *output = '\0';
+
+ *start = tail;
+
+ return;
+}
+
+/****************************************************************************
+ *
+ *
+ ***************************************************************************/
+void Serenum_GetDevSerialNo(
+ _In_reads_(len) PCHAR input,
+ int len,
+ _Out_writes_(MAX_DEVNODE_NAME) LPSTR output,
+ _Inout_ int *start
+ )
+{
+
+ int tail, cnt;
+ char c;
+
+ PAGED_CODE();
+
+ *output = '\0';
+
+ tail = *start;
+
+ if ( tail >= len ) { return; }
+
+ if( input[tail++] != '\\')
+ return;
+
+ if ( tail >= len ) { *start = tail-1; return; }
+ c = input[tail++];
+
+ cnt = 0;
+
+ while(cnt < 8 && tail < 256 && ( c != '\\') && ( c != ')') && tail < len ) {
+ cnt++;
+ if(Serenum_HToI(c) < 0)
+ break;
+
+ *output++ = c;
+ c = input[tail++];
+ }
+
+ *output = '\0';
+
+ *start = tail - 1;
+
+ return;
+}
+
+/****************************************************************************
+ *
+ *
+ ***************************************************************************/
+void Serenum_GetDevClass(
+ _In_reads_(len) PCHAR input,
+ int len,
+ _Out_writes_(MAX_DEVNODE_NAME) LPSTR output,
+ _Inout_ int *start
+ )
+{
+
+ int tail, cnt;
+ char c;
+
+ PAGED_CODE();
+
+ *output = '\0';
+
+ tail = *start;
+
+ if ( tail >= len ) { return; }
+
+ if( input[tail++] != '\\')
+ return;
+
+ if ( tail >= len ) { *start = tail-1; return; }
+ c = input[tail++];
+
+ cnt = 0;
+
+ while(tail < 256 && ( c != '\\') && ( c != ')') && tail < len && cnt < MAX_DEVNODE_NAME - 1) {
+ cnt++;
+ *output++ = c;
+ c = input[tail++];
+ }
+ *output = '\0';
+
+ *start = tail - 1;
+
+ return;
+}
+
+
+void Serenum_GetDevCompId(
+ _In_reads_(len) PCHAR input,
+ int len,
+ _Out_writes_(MAX_DEVNODE_NAME) LPSTR output,
+ _Inout_ int *start
+ )
+/****************************************************************************
+ *
+ *
+ ***************************************************************************/
+{
+
+ int tail, cnt;
+ char c;
+
+ PAGED_CODE();
+
+ *output = '\0';
+
+ tail = *start;
+
+ if ( tail >= len ) { return; }
+
+ if( input[tail++] != '\\')
+ return;
+
+ if ( tail >= len ) { *start = tail-1; return; }
+ c = input[tail++];
+
+ cnt = 0;
+
+ while(tail < 256 && ( c != '\\') && ( c != ')') && tail < len && cnt < MAX_DEVNODE_NAME - 2 ) {
+ cnt++;
+ *output++ = c;
+ //
+ // Put a * after every comma
+ //
+ if ('\x0C' == c || '\x2C' == c) {
+ cnt++;
+ *output++ = '*';
+ }
+ c = input[tail++];
+ }
+
+ *output = '\0';
+
+ *start = tail - 1;
+}
+
+void
+Serenum_GetDevDesc(
+ _In_reads_(len) PCHAR input,
+ int len,
+ _Out_writes_(MAX_DEVNODE_NAME) LPSTR output,
+ _Inout_ int *start
+ )
+/****************************************************************************
+ *
+ *
+ ***************************************************************************/
+{
+
+ int tail, cnt;
+ char c;
+
+ PAGED_CODE();
+
+ *output = '\0';
+
+ tail = *start;
+
+ if ( tail >= len ) { return; }
+
+ if( input[tail++] != '\\')
+ return;
+
+ if ( tail >= len ) { *start = tail-1; return; }
+ c = input[tail++];
+
+ cnt = 0;
+
+ while(tail < 256 && ( c != '\\') && ( c != ')') && tail < len && cnt < MAX_DEVNODE_NAME - 1) {
+ cnt++;
+ *output++ = c;
+ c = input[tail++];
+ }
+
+ *output = '\0';
+
+ *start = tail - 1;
+}
+
+
diff --git a/serial/serial/ReadMe.md b/serial/serial/ReadMe.md
new file mode 100644
index 00000000..e1008572
--- /dev/null
+++ b/serial/serial/ReadMe.md
@@ -0,0 +1,35 @@
+Serial Port Driver
+==================
+
+The Serial (16550-based RS-232) sample driver is a WDF version of the inbox Serial.sys driver in %WINDIR%\\system32\\drivers.
+
+## Universal Compliant
+This sample builds a Windows Universal driver. It uses only APIs and DDIs that are included in Windows Core.
+
+This sample driver is functionally equivalent to the inbox driver, with these two exceptions:
+
+1. This sample does not support multi-function serial devices.
+2. This sample does not support legacy serial ports. Legacy ports are not detected by the BIOS, and are, therefore, not enumerated by the operating system.
+
+The Serial sample driver runs in kernel mode.
+
+This sample driver supports power management. When a serial port is not in use, the driver places the port hardware in a low-power state. When the port is opened, it receives power and wakes up. The driver supports wake-on-ring for platforms that support this function. The driver can be compiled to run on both 32-bit and 64-bit versions of Windows.
+
+For more information, see [Features of Serial and Serenum](http://msdn.microsoft.com/en-us/library/windows/hardware/ff546505).
+
+This sample can be used for these hardware IDs without any modification to the .inx file included in the project.
+
+- PNP0501
+- PNP0500
+
+ If you have other hardware such as an add-in card, then you must add the hardware ID in the .inx as shown in this example. Then, you must build the project as per the instructions given in the Building the sample section in this readme.
+
+ ``` {.syntax xml:space="preserve"}
+ ; For XP and later
+ [MSFT.NTamd64]
+ ; DisplayName Section DeviceId
+ ; ----------- ------- --------
+ %PNP0500.DevDesc%= Serial_Inst, *PNP0500, *PNP0501 ; Communications Port
+ %PNP0501.DevDesc%= Serial_Inst, *PNP0501, *PNP0500 ; Communications Port
+ %PNP0501.DevDesc%= Serial_Inst, MF\PCI9710_COM ; Communications Port
+ ```
diff --git a/serial/serial/error.c b/serial/serial/error.c
new file mode 100644
index 00000000..8acdb077
--- /dev/null
+++ b/serial/serial/error.c
@@ -0,0 +1,67 @@
+/*++
+Copyright (c) Microsoft Corporation
+
+Module Name:
+
+ error.c
+
+Abstract:
+
+ This module contains the code that is very specific to error
+ operations in the serial driver
+
+Environment:
+
+ Kernel mode
+
+--*/
+
+#include "precomp.h"
+
+#if defined(EVENT_TRACING)
+#include "error.tmh"
+#endif
+
+
+VOID
+SerialCommError(
+ IN WDFDPC Dpc
+ )
+/*++
+
+Routine Description:
+
+ This routine is invoked at dpc level to in response to
+ a comm error. All comm errors complete all read and writes
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PSERIAL_DEVICE_EXTENSION Extension = NULL;
+
+ Extension = SerialGetDeviceExtension(WdfDpcGetParentObject(Dpc));
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_INIT,
+ ">SerialCommError(%p)\n", Extension);
+
+ SerialFlushRequests(
+ Extension->WriteQueue,
+ &Extension->CurrentWriteRequest
+ );
+
+ SerialFlushRequests(
+ Extension->ReadQueue,
+ &Extension->CurrentReadRequest
+ );
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_INIT,
+ "<SerialCommError\n");
+}
+
+
diff --git a/serial/serial/flush.c b/serial/serial/flush.c
new file mode 100644
index 00000000..47812df8
--- /dev/null
+++ b/serial/serial/flush.c
@@ -0,0 +1,86 @@
+/*++
+
+Copyright (c) 1991, 1992, 1993 Microsoft Corporation
+
+Module Name:
+
+ flush.c
+
+Abstract:
+
+ This module contains the code that is very specific to flush
+ operations in the serial driver
+
+Environment:
+
+ Kernel mode
+
+--*/
+
+#include "precomp.h"
+
+#if defined(EVENT_TRACING)
+#include "flush.tmh"
+#endif
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE, SerialFlush)
+#endif
+
+#pragma warning(push)
+#pragma warning(disable:28118) // this callback will run at IRQL=PASSIVE_LEVEL
+_Use_decl_annotations_
+NTSTATUS
+SerialFlush(
+ WDFDEVICE Device,
+ PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This is the dispatch routine for flush. Flushing works by placing
+ this request in the write queue. When this request reaches the
+ front of the write queue we simply complete it since this implies
+ that all previous writes have completed.
+
+Arguments:
+
+ DeviceObject - Pointer to the device object for this device
+
+ Irp - Pointer to the IRP for the current request
+
+Return Value:
+
+ Could return status success, cancelled, or pending.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION extension;
+
+ extension = SerialGetDeviceExtension(Device);
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_WRITE, ">SerialFlush(%p, %p)\n", Device, Irp);
+
+ PAGED_CODE();
+
+ WdfIoQueueStopSynchronously(extension->WriteQueue);
+ //
+ // Flush is done - restart the queue
+ //
+ WdfIoQueueStart(extension->WriteQueue);
+
+ Irp->IoStatus.Information = 0L;
+ Irp->IoStatus.Status = STATUS_SUCCESS;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_WRITE, "<SerialFlush\n");
+
+ return STATUS_SUCCESS;
+ }
+#pragma warning(pop) // enable 28118 again
+
diff --git a/serial/serial/immediat.c b/serial/serial/immediat.c
new file mode 100644
index 00000000..a7135c86
--- /dev/null
+++ b/serial/serial/immediat.c
@@ -0,0 +1,458 @@
+/*++
+
+Copyright (c) 1991, 1992, 1993 - 1997 Microsoft Corporation
+
+Module Name:
+
+ immediat.c
+
+Abstract:
+
+ This module contains the code that is very specific to transmit
+ immediate character operations in the serial driver
+
+Environment:
+
+ Kernel mode
+
+--*/
+
+#include "precomp.h"
+
+#if defined(EVENT_TRACING)
+#include "immediat.tmh"
+#endif
+
+
+VOID
+SerialGetNextImmediate(
+ IN WDFREQUEST *CurrentOpRequest,
+ IN WDFQUEUE QueueToProcess,
+ IN WDFREQUEST *NewRequest,
+ IN BOOLEAN CompleteCurrent,
+ IN PSERIAL_DEVICE_EXTENSION Extension
+ );
+
+EVT_WDF_REQUEST_CANCEL SerialCancelImmediate;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialGiveImmediateToIsr;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialGrabImmediateFromIsr;
+
+
+VOID
+SerialStartImmediate(
+ IN PSERIAL_DEVICE_EXTENSION Extension
+ )
+
+/*++
+
+Routine Description:
+
+ This routine will calculate the timeouts needed for the
+ write. It will then hand the request off to the isr. It
+ will need to be careful incase the request has been canceled.
+
+Arguments:
+
+ Extension - A pointer to the serial device extension.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LARGE_INTEGER TotalTime = {0};
+ BOOLEAN UseATimer;
+ SERIAL_TIMEOUTS Timeouts;
+ PREQUEST_CONTEXT reqContext;
+
+ reqContext = SerialGetRequestContext(Extension->CurrentImmediateRequest);
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS, ">SerialStartImmediate(%p)\n",
+ Extension);
+
+ UseATimer = FALSE;
+ reqContext->Status = STATUS_PENDING;
+
+ //
+ // Calculate the timeout value needed for the
+ // request. Note that the values stored in the
+ // timeout record are in milliseconds. Note that
+ // if the timeout values are zero then we won't start
+ // the timer.
+ //
+
+ Timeouts = Extension->Timeouts;
+
+ if (Timeouts.WriteTotalTimeoutConstant ||
+ Timeouts.WriteTotalTimeoutMultiplier) {
+
+ UseATimer = TRUE;
+
+ //
+ // We have some timer values to calculate.
+ //
+
+ TotalTime.QuadPart
+ = (LONGLONG)((ULONG)Timeouts.WriteTotalTimeoutMultiplier);
+
+ TotalTime.QuadPart += Timeouts.WriteTotalTimeoutConstant;
+
+ TotalTime.QuadPart *= -10000;
+
+ }
+
+ //
+ // As the request might be going to the isr, this is a good time
+ // to initialize the reference count.
+ //
+
+ SERIAL_INIT_REFERENCE(reqContext);
+
+ //
+ // We give the request to to the isr to write out.
+ // We set a cancel routine that knows how to
+ // grab the current write away from the isr.
+ //
+ SerialSetCancelRoutine(Extension->CurrentImmediateRequest,
+ SerialCancelImmediate);
+
+ if (UseATimer) {
+ BOOLEAN result;
+
+ result = SerialSetTimer(
+ Extension->ImmediateTotalTimer,
+ TotalTime
+ );
+
+ if(result == FALSE) {
+ //
+ // Since the timer knows about the request we increment
+ // the reference count.
+ //
+
+ SERIAL_SET_REFERENCE(
+ reqContext,
+ SERIAL_REF_TOTAL_TIMER
+ );
+ }
+ }
+
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SerialGiveImmediateToIsr,
+ Extension
+ );
+
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS,
+ "<SerialStartImmediate\n");
+
+}
+
+VOID
+SerialCompleteImmediate(
+ IN WDFDPC Dpc
+ )
+
+{
+
+ PSERIAL_DEVICE_EXTENSION Extension = NULL;
+
+ Extension = SerialGetDeviceExtension(WdfDpcGetParentObject(Dpc));
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS, ">SerialCompleteImmediate(%p)\n",
+ Extension);
+
+ SerialTryToCompleteCurrent(
+ Extension,
+ NULL,
+ STATUS_SUCCESS,
+ &Extension->CurrentImmediateRequest,
+ NULL,
+ NULL,
+ Extension->ImmediateTotalTimer,
+ NULL,
+ SerialGetNextImmediate,
+ SERIAL_REF_ISR
+ );
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS, "<SerialCompleteImmediate\n");
+
+}
+
+VOID
+SerialTimeoutImmediate(
+ IN WDFTIMER Timer
+ )
+{
+
+ PSERIAL_DEVICE_EXTENSION Extension = NULL;
+
+ Extension = SerialGetDeviceExtension(WdfTimerGetParentObject(Timer));
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS, ">SerialTimeoutImmediate(%p)\n",
+ Extension);
+
+ SerialTryToCompleteCurrent(
+ Extension,
+ SerialGrabImmediateFromIsr,
+ STATUS_TIMEOUT,
+ &Extension->CurrentImmediateRequest,
+ NULL,
+ NULL,
+ Extension->ImmediateTotalTimer,
+ NULL,
+ SerialGetNextImmediate,
+ SERIAL_REF_TOTAL_TIMER
+ );
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS, "<SerialTimeoutImmediate\n");
+}
+
+VOID
+SerialGetNextImmediate(
+ IN WDFREQUEST *CurrentOpRequest,
+ IN WDFQUEUE QueueToProcess,
+ IN WDFREQUEST *NewRequest,
+ IN BOOLEAN CompleteCurrent,
+ IN PSERIAL_DEVICE_EXTENSION Extension
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to complete the current immediate
+ request. Even though the current immediate will always
+ be completed and there is no queue associated with it,
+ we use this routine so that we can try to satisfy
+ a wait for transmit queue empty event.
+
+Arguments:
+
+ CurrentOpRequest - Pointer to the pointer that points to the
+ current write request. This should point
+ to CurrentImmediateRequest.
+
+ QueueToProcess - Always NULL.
+
+ NewRequest - Always NULL on exit to this routine.
+
+ CompleteCurrent - Should always be true for this routine.
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ WDFREQUEST oldRequest = *CurrentOpRequest;
+ PREQUEST_CONTEXT reqContext = SerialGetRequestContext(oldRequest);
+
+ UNREFERENCED_PARAMETER(QueueToProcess);
+ UNREFERENCED_PARAMETER(CompleteCurrent);
+
+
+ ASSERT(Extension->TotalCharsQueued >= 1);
+ Extension->TotalCharsQueued--;
+
+ *CurrentOpRequest = NULL;
+ *NewRequest = NULL;
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SerialProcessEmptyTransmit,
+ Extension
+ );
+
+ SerialCompleteRequest(oldRequest, reqContext->Status, reqContext->Information);
+}
+
+VOID
+SerialCancelImmediate(
+ IN WDFREQUEST Request
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to cancel a request that is waiting on
+ a comm event.
+
+Arguments:
+
+ Request - Pointer to the WDFREQUEST for the current request
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PSERIAL_DEVICE_EXTENSION Extension = NULL;
+ WDFDEVICE device = WdfIoQueueGetDevice(WdfRequestGetIoQueue(Request));
+
+ UNREFERENCED_PARAMETER(Request);
+
+ Extension = SerialGetDeviceExtension(device);
+
+ SerialTryToCompleteCurrent(
+ Extension,
+ SerialGrabImmediateFromIsr,
+ STATUS_CANCELLED,
+ &Extension->CurrentImmediateRequest,
+ NULL,
+ NULL,
+ Extension->ImmediateTotalTimer,
+ NULL,
+ SerialGetNextImmediate,
+ SERIAL_REF_CANCEL
+ );
+
+}
+
+BOOLEAN
+SerialGiveImmediateToIsr(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+/*++
+
+Routine Description:
+
+ Try to start off the write by slipping it in behind
+ a transmit immediate char, or if that isn't available
+ and the transmit holding register is empty, "tickle"
+ the UART into interrupting with a transmit buffer
+ empty.
+
+ NOTE: This routine is called by WdfInterruptSynchronize.
+
+ NOTE: This routine assumes that it is called with the
+ cancel spin lock held.
+
+Arguments:
+
+ Context - Really a pointer to the device extension.
+
+Return Value:
+
+ This routine always returns FALSE.
+
+--*/
+{
+ PSERIAL_DEVICE_EXTENSION Extension = Context;
+ PREQUEST_CONTEXT reqContext;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ reqContext = SerialGetRequestContext(Extension->CurrentImmediateRequest);
+
+ Extension->TransmitImmediate = TRUE;
+ Extension->ImmediateChar = *((UCHAR *) (reqContext->SystemBuffer));
+
+ //
+ // The isr now has a reference to the request.
+ //
+
+ SERIAL_SET_REFERENCE(
+ reqContext,
+ SERIAL_REF_ISR
+ );
+
+ //
+ // Check first to see if a write is going on. If
+ // there is then we'll just slip in during the write.
+ //
+
+ if (!Extension->WriteLength) {
+
+ //
+ // If there is no normal write transmitting then we
+ // will "re-enable" the transmit holding register empty
+ // interrupt. The 8250 family of devices will always
+ // signal a transmit holding register empty interrupt
+ // *ANY* time this bit is set to one. By doing things
+ // this way we can simply use the normal interrupt code
+ // to start off this write.
+ //
+ // We've been keeping track of whether the transmit holding
+ // register is empty so it we only need to do this
+ // if the register is empty.
+ //
+
+ if (Extension->HoldingEmpty) {
+ DISABLE_ALL_INTERRUPTS(Extension, Extension->Controller);
+ ENABLE_ALL_INTERRUPTS(Extension, Extension->Controller);
+
+ }
+
+ }
+
+ return FALSE;
+
+}
+
+BOOLEAN
+SerialGrabImmediateFromIsr(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+
+ This routine is used to grab the current request, which could be timing
+ out or canceling, from the ISR
+
+ NOTE: This routine is being called from WdfInterruptSynchronize.
+
+ NOTE: This routine assumes that the cancel spin lock is held
+ when this routine is called.
+
+Arguments:
+
+ Context - Really a pointer to the device extension.
+
+Return Value:
+
+ Always false.
+
+--*/
+
+{
+ PSERIAL_DEVICE_EXTENSION Extension = Context;
+ PREQUEST_CONTEXT reqContext;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ reqContext = SerialGetRequestContext(Extension->CurrentImmediateRequest);
+
+ if (Extension->TransmitImmediate) {
+
+ Extension->TransmitImmediate = FALSE;
+
+ //
+ // Since the isr no longer references this request, we can
+ // decrement it's reference count.
+ //
+
+ SERIAL_CLEAR_REFERENCE(
+ reqContext,
+ SERIAL_REF_ISR
+ );
+
+ }
+
+ return FALSE;
+
+}
+
+
diff --git a/serial/serial/initunlo.c b/serial/serial/initunlo.c
new file mode 100644
index 00000000..1f890e38
--- /dev/null
+++ b/serial/serial/initunlo.c
@@ -0,0 +1,197 @@
+/*++
+
+Copyright (c) Microsoft Corporation
+
+Module Name:
+
+ initunlo.c
+
+Abstract:
+
+ This module contains the code that is very specific to initialization
+ and unload operations in the serial driver
+
+ WDF Version of serial sample doesn't support:
+ 1) Multiport Serial devices.
+ 2) Enumeration of Non PNP serial devices that are not detected by BIOS
+ (IO address range 0x2F0-0x2F7 using IRQ 9)
+Environment:
+
+ Kernel mode
+
+--*/
+
+#include "precomp.h"
+
+#if defined(EVENT_TRACING)
+#include "initunlo.tmh"
+#endif
+
+static const PHYSICAL_ADDRESS SerialPhysicalZero = {0};
+
+//
+// We use this to query into the registry as to whether we
+// should break at driver entry.
+//
+
+SERIAL_FIRMWARE_DATA driverDefaults;
+
+//
+// This is exported from the kernel. It is used to point
+// to the address that the kernel debugger is using.
+//
+extern PUCHAR *KdComPortInUse;
+//
+// INIT - only needed during init and then can be disposed
+// PAGESRP0 - always paged / never locked
+// PAGESER - must be locked when a device is open, else paged
+//
+//
+// INIT is used for DriverEntry() specific code
+//
+// PAGESRP0 is used for code that is not often called and has nothing
+// to do with I/O performance. An example, passive-level PNP
+// support functions
+//
+// PAGESER is used for code that needs to be locked after an open for both
+// performance and IRQL reasons.
+//
+
+ULONG DebugLevel = TRACE_LEVEL_INFORMATION;
+ULONG DebugFlag = 0xf;//0x46;//0x4FF; //0x00000006;
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(INIT, DriverEntry)
+#pragma alloc_text(PAGE, SerialEvtDriverContextCleanup)
+#endif
+
+
+
+NTSTATUS
+DriverEntry(
+ IN PDRIVER_OBJECT DriverObject,
+ IN PUNICODE_STRING RegistryPath
+ )
+/*++
+
+Routine Description:
+
+ The entry point that the system point calls to initialize
+ any driver.
+
+Arguments:
+
+ DriverObject - Just what it says, really of little use
+ to the driver itself, it is something that the IO system
+ cares more about.
+
+ PathToRegistry - points to the entry for this driver
+ in the current control set of the registry.
+
+Return Value:
+
+ Always STATUS_SUCCESS
+
+--*/
+
+{
+ WDF_DRIVER_CONFIG config;
+ WDFDRIVER hDriver;
+ NTSTATUS status;
+ WDF_OBJECT_ATTRIBUTES attributes;
+
+ //
+ // Initialize WPP Tracing
+ //
+ WPP_INIT_TRACING( DriverObject, RegistryPath );
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_INIT,
+ "Serial Sample (WDF Version)\n");
+ //
+ // 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 = SerialEvtDriverContextCleanup;
+
+ WDF_DRIVER_CONFIG_INIT(&config, SerialEvtDeviceAdd);
+
+ status = WdfDriverCreate(DriverObject,
+ RegistryPath,
+ &attributes,
+ &config,
+ &hDriver);
+ if (!NT_SUCCESS(status)) {
+ SerialDbgPrintEx(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);
+ return status;
+ }
+
+ //
+ // Call to find out default values to use for all the devices that the
+ // driver controls, including whether or not to break on entry.
+ //
+
+ SerialGetConfigDefaults(&driverDefaults, hDriver);
+
+ //
+ // Break on entry if requested via registry
+ //
+ if (driverDefaults.ShouldBreakOnEntry) {
+ DbgBreakPoint();
+ }
+
+
+ return status;
+}
+
+
+_Use_decl_annotations_
+VOID
+SerialEvtDriverContextCleanup(
+ WDFOBJECT Driver
+ )
+/*++
+Routine Description:
+
+ Free all the resources allocated in DriverEntry.
+
+Arguments:
+
+ Driver - handle to a WDF Driver object.
+
+Return Value:
+
+ VOID.
+
+--*/
+{
+ UNREFERENCED_PARAMETER(Driver);
+
+ PAGED_CODE ();
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_INIT,
+ "--> SerialEvtDriverContextCleanup\n");
+
+ //
+ // Stop WPP Tracing
+ //
+ WPP_CLEANUP( WdfDriverWdmGetDriverObject(Driver) );
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_INIT,
+ "<-- SerialEvtDriverContextCleanup\n");
+
+}
+
+
+
diff --git a/serial/serial/ioctl.c b/serial/serial/ioctl.c
new file mode 100644
index 00000000..3fd2d8f6
--- /dev/null
+++ b/serial/serial/ioctl.c
@@ -0,0 +1,2195 @@
+/*++
+
+Copyright (c) Microsoft Corporation
+
+Module Name:
+
+ ioctl.c
+
+Abstract:
+
+ This module contains the ioctl dispatcher as well as a couple
+ of routines that are generally just called in response to
+ ioctl calls.
+
+Environment:
+
+ Kernel mode
+
+--*/
+
+#include "precomp.h"
+
+#if defined(EVENT_TRACING)
+#include "ioctl.tmh"
+#endif
+
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialGetModemUpdate;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialGetCommStatus;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialSetEscapeChar;
+
+PCHAR
+SerialGetIoctlName(
+ IN ULONG IoControlCode
+ )
+/*++
+
+Routine Description:
+ SerialGetIoctlName returns the name of the ioctl
+
+--*/
+{
+ switch (IoControlCode)
+ {
+ case IOCTL_SERIAL_SET_BAUD_RATE : return "IOCTL_SERIAL_SET_BAUD_RATE";
+ case IOCTL_SERIAL_GET_BAUD_RATE: return "IOCTL_SERIAL_GET_BAUD_RATE";
+ case IOCTL_SERIAL_GET_MODEM_CONTROL: return "IOCTL_SERIAL_GET_MODEM_CONTROL";
+ case IOCTL_SERIAL_SET_MODEM_CONTROL: return "IOCTL_SERIAL_SET_MODEM_CONTROL";
+ case IOCTL_SERIAL_SET_FIFO_CONTROL: return "IOCTL_SERIAL_SET_FIFO_CONTROL";
+ case IOCTL_SERIAL_SET_LINE_CONTROL: return "IOCTL_SERIAL_SET_LINE_CONTROL";
+ case IOCTL_SERIAL_GET_LINE_CONTROL: return "IOCTL_SERIAL_GET_LINE_CONTROL";
+ case IOCTL_SERIAL_SET_TIMEOUTS: return "IOCTL_SERIAL_SET_TIMEOUTS";
+ case IOCTL_SERIAL_GET_TIMEOUTS: return "IOCTL_SERIAL_GET_TIMEOUTS";
+ case IOCTL_SERIAL_SET_CHARS: return "IOCTL_SERIAL_SET_CHARS";
+ case IOCTL_SERIAL_GET_CHARS: return "IOCTL_SERIAL_GET_CHARS";
+ case IOCTL_SERIAL_SET_DTR: return "IOCTL_SERIAL_SET_DTR";
+ case IOCTL_SERIAL_CLR_DTR: return "IOCTL_SERIAL_SET_DTR";
+ case IOCTL_SERIAL_RESET_DEVICE: return "IOCTL_SERIAL_RESET_DEVICE";
+ case IOCTL_SERIAL_SET_RTS: return "IOCTL_SERIAL_SET_RTS";
+ case IOCTL_SERIAL_CLR_RTS: return "IOCTL_SERIAL_CLR_RTS";
+ case IOCTL_SERIAL_SET_XOFF: return "IOCTL_SERIAL_SET_XOFF";
+ case IOCTL_SERIAL_SET_XON: return "IOCTL_SERIAL_SET_XON";
+ case IOCTL_SERIAL_SET_BREAK_ON: return "IOCTL_SERIAL_SET_BREAK_ON";
+ case IOCTL_SERIAL_SET_BREAK_OFF: return "IOCTL_SERIAL_SET_BREAK_OFF";
+ case IOCTL_SERIAL_SET_QUEUE_SIZE: return "IOCTL_SERIAL_SET_QUEUE_SIZE";
+ case IOCTL_SERIAL_GET_WAIT_MASK: return "IOCTL_SERIAL_GET_WAIT_MASK";
+ case IOCTL_SERIAL_SET_WAIT_MASK: return "IOCTL_SERIAL_SET_WAIT_MASK";
+ case IOCTL_SERIAL_WAIT_ON_MASK: return "IOCTL_SERIAL_WAIT_ON_MASK";
+ case IOCTL_SERIAL_IMMEDIATE_CHAR: return "IOCTL_SERIAL_IMMEDIATE_CHAR";
+ case IOCTL_SERIAL_PURGE: return "IOCTL_SERIAL_PURGE";
+ case IOCTL_SERIAL_GET_HANDFLOW: return "IOCTL_SERIAL_GET_HANDFLOW";
+ case IOCTL_SERIAL_SET_HANDFLOW: return "IOCTL_SERIAL_SET_HANDFLOW";
+ case IOCTL_SERIAL_GET_MODEMSTATUS: return "IOCTL_SERIAL_GET_MODEMSTATUS";
+ case IOCTL_SERIAL_GET_DTRRTS: return "IOCTL_SERIAL_GET_DTRRTS";
+ case IOCTL_SERIAL_GET_COMMSTATUS: return "IOCTL_SERIAL_GET_COMMSTATUS";
+ case IOCTL_SERIAL_GET_PROPERTIES: return "IOCTL_SERIAL_GET_PROPERTIES";
+ case IOCTL_SERIAL_XOFF_COUNTER: return "IOCTL_SERIAL_XOFF_COUNTER";
+ case IOCTL_SERIAL_LSRMST_INSERT: return "IOCTL_SERIAL_LSRMST_INSERT";
+ case IOCTL_SERIAL_CONFIG_SIZE: return "IOCTL_SERIAL_CONFIG_SIZE";
+ case IOCTL_SERIAL_GET_STATS: return "IOCTL_SERIAL_GET_STATS";
+ case IOCTL_SERIAL_CLEAR_STATS: return "IOCTL_SERIAL_CLEAR_STATS";
+ default: return "UnKnown ioctl";
+ }
+}
+
+
+
+BOOLEAN
+SerialGetStats(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ In sync with the interrpt service routine (which sets the perf stats)
+ return the perf stats to the caller.
+
+
+Arguments:
+
+ Context - Pointer to a the request.
+
+Return Value:
+
+ This routine always returns FALSE.
+
+--*/
+
+{
+ PREQUEST_CONTEXT reqContext = (PREQUEST_CONTEXT)Context;
+ PSERIAL_DEVICE_EXTENSION extension = SerialGetDeviceExtension(WdfInterruptGetDevice(Interrupt));
+ PSERIALPERF_STATS sp = reqContext->SystemBuffer;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ *sp = extension->PerfStats;
+ return FALSE;
+
+}
+
+
+BOOLEAN
+SerialClearStats(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ In sync with the interrpt service routine (which sets the perf stats)
+ clear the perf stats.
+
+
+Arguments:
+
+ Context - Pointer to a the extension.
+
+Return Value:
+
+ This routine always returns FALSE.
+
+--*/
+
+{
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ RtlZeroMemory(
+ &((PSERIAL_DEVICE_EXTENSION)Context)->PerfStats,
+ sizeof(SERIALPERF_STATS)
+ );
+
+ RtlZeroMemory(&((PSERIAL_DEVICE_EXTENSION)Context)->WmiPerfData,
+ sizeof(SERIAL_WMI_PERF_DATA));
+
+ return FALSE;
+}
+
+
+
+BOOLEAN
+SerialSetChars(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to set the special characters for the
+ driver.
+
+Arguments:
+
+ Context - Pointer to a structure that contains a pointer to
+ the device extension and a pointer to a special characters
+ structure.
+
+Return Value:
+
+ This routine always returns FALSE.
+
+--*/
+
+{
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ ((PSERIAL_IOCTL_SYNC)Context)->Extension->SpecialChars =
+ *((PSERIAL_CHARS)(((PSERIAL_IOCTL_SYNC)Context)->Data));
+
+ return FALSE;
+}
+
+
+BOOLEAN
+SerialSetBaud(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to set the baud rate of the device.
+
+Arguments:
+
+ Context - Pointer to a structure that contains a pointer to
+ the device extension and what should be the current
+ baud rate.
+
+Return Value:
+
+ This routine always returns FALSE.
+
+--*/
+
+{
+ PSERIAL_DEVICE_EXTENSION Extension = ((PSERIAL_IOCTL_SYNC)Context)->Extension;
+ USHORT Appropriate = PtrToUshort(((PSERIAL_IOCTL_SYNC)Context)->Data);
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ WRITE_DIVISOR_LATCH(
+ Extension,
+ Extension->Controller,
+ Appropriate
+ );
+
+ return FALSE;
+}
+
+
+BOOLEAN
+SerialSetLineControl(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to set the buad rate of the device.
+
+Arguments:
+
+ Context - Pointer to the device extension.
+
+Return Value:
+
+ This routine always returns FALSE.
+
+--*/
+
+{
+ PSERIAL_DEVICE_EXTENSION Extension = Context;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ WRITE_LINE_CONTROL(Extension,
+ Extension->Controller,
+ Extension->LineControl
+ );
+
+ return FALSE;
+}
+
+
+BOOLEAN
+SerialGetModemUpdate(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is simply used to call the interrupt level routine
+ that handles modem status update.
+
+Arguments:
+
+ Context - Pointer to a structure that contains a pointer to
+ the device extension and a pointer to a ulong.
+
+Return Value:
+
+ This routine always returns FALSE.
+
+--*/
+
+{
+ PSERIAL_DEVICE_EXTENSION Extension = ((PSERIAL_IOCTL_SYNC)Context)->Extension;
+ ULONG *Result = (ULONG *)(((PSERIAL_IOCTL_SYNC)Context)->Data);
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ *Result = SerialHandleModemUpdate(
+ Extension,
+ FALSE
+ );
+
+ return FALSE;
+}
+
+
+
+BOOLEAN
+SerialSetMCRContents(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+/*++
+
+Routine Description:
+
+ This routine is simply used to set the contents of the MCR
+
+Arguments:
+
+ Context - Pointer to a structure that contains a pointer to
+ the device extension and a pointer to a ulong.
+
+Return Value:
+
+ This routine always returns FALSE.
+
+--*/
+{
+ PSERIAL_DEVICE_EXTENSION Extension = ((PSERIAL_IOCTL_SYNC)Context)->Extension;
+ ULONG *Result = (ULONG *)(((PSERIAL_IOCTL_SYNC)Context)->Data);
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ //
+ // This is severe casting abuse!!!
+ //
+ WRITE_MODEM_CONTROL(Extension, Extension->Controller, (UCHAR)PtrToUlong(Result));
+
+ return FALSE;
+}
+
+
+
+
+BOOLEAN
+SerialGetMCRContents(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is simply used to get the contents of the MCR
+
+Arguments:
+
+ Context - Pointer to a structure that contains a pointer to
+ the device extension and a pointer to a ulong.
+
+Return Value:
+
+ This routine always returns FALSE.
+
+--*/
+
+{
+ PSERIAL_DEVICE_EXTENSION Extension = ((PSERIAL_IOCTL_SYNC)Context)->Extension;
+ ULONG *Result = (ULONG *)(((PSERIAL_IOCTL_SYNC)Context)->Data);
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ *Result = READ_MODEM_CONTROL(Extension, Extension->Controller);
+
+ return FALSE;
+}
+
+
+
+
+BOOLEAN
+SerialSetFCRContents(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+/*++
+
+Routine Description:
+
+ This routine is simply used to set the contents of the FCR
+
+Arguments:
+
+ Context - Pointer to a structure that contains a pointer to
+ the device extension and a pointer to a ulong.
+
+Return Value:
+
+ This routine always returns FALSE.
+
+--*/
+{
+ PSERIAL_DEVICE_EXTENSION Extension = ((PSERIAL_IOCTL_SYNC)Context)->Extension;
+ ULONG *Result = (ULONG *)(((PSERIAL_IOCTL_SYNC)Context)->Data);
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ //
+ // This is severe casting abuse!!!
+ //
+ WRITE_FIFO_CONTROL(Extension, Extension->Controller, (UCHAR)*Result);
+
+ return FALSE;
+}
+
+
+
+BOOLEAN
+SerialGetCommStatus(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This is used to get the current state of the serial driver.
+
+Arguments:
+
+ Context - Pointer to a structure that contains a pointer to
+ the device extension and a pointer to a serial status
+ record.
+
+Return Value:
+
+ This routine always returns FALSE.
+
+--*/
+
+{
+ PSERIAL_DEVICE_EXTENSION Extension = ((PSERIAL_IOCTL_SYNC)Context)->Extension;
+ PSERIAL_STATUS Stat = ((PSERIAL_IOCTL_SYNC)Context)->Data;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ Stat->Errors = Extension->ErrorWord;
+ Extension->ErrorWord = 0;
+
+ //
+ // Eof isn't supported in binary mode
+ //
+ Stat->EofReceived = FALSE;
+
+ Stat->AmountInInQueue = Extension->CharsInInterruptBuffer;
+
+ Stat->AmountInOutQueue = Extension->TotalCharsQueued;
+
+ if (Extension->WriteLength) {
+
+ //
+ // By definition if we have a writelength the we have
+ // a current write request.
+ //
+ PREQUEST_CONTEXT reqContext = NULL;
+
+ ASSERT(Extension->CurrentWriteRequest);
+ ASSERT(Stat->AmountInOutQueue >= Extension->WriteLength);
+
+ reqContext = SerialGetRequestContext(Extension->CurrentWriteRequest);
+ Stat->AmountInOutQueue -= reqContext->Length - (Extension->WriteLength);
+
+ }
+
+ Stat->WaitForImmediate = Extension->TransmitImmediate;
+
+ Stat->HoldReasons = 0;
+ if (Extension->TXHolding) {
+
+ if (Extension->TXHolding & SERIAL_TX_CTS) {
+
+ Stat->HoldReasons |= SERIAL_TX_WAITING_FOR_CTS;
+
+ }
+
+ if (Extension->TXHolding & SERIAL_TX_DSR) {
+
+ Stat->HoldReasons |= SERIAL_TX_WAITING_FOR_DSR;
+
+ }
+
+ if (Extension->TXHolding & SERIAL_TX_DCD) {
+
+ Stat->HoldReasons |= SERIAL_TX_WAITING_FOR_DCD;
+
+ }
+
+ if (Extension->TXHolding & SERIAL_TX_XOFF) {
+
+ Stat->HoldReasons |= SERIAL_TX_WAITING_FOR_XON;
+
+ }
+
+ if (Extension->TXHolding & SERIAL_TX_BREAK) {
+
+ Stat->HoldReasons |= SERIAL_TX_WAITING_ON_BREAK;
+
+ }
+
+ }
+
+ if (Extension->RXHolding & SERIAL_RX_DSR) {
+
+ Stat->HoldReasons |= SERIAL_RX_WAITING_FOR_DSR;
+
+ }
+
+ if (Extension->RXHolding & SERIAL_RX_XOFF) {
+
+ Stat->HoldReasons |= SERIAL_TX_WAITING_XOFF_SENT;
+
+ }
+
+ return FALSE;
+}
+
+
+BOOLEAN
+SerialSetEscapeChar(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This is used to set the character that will be used to escape
+ line status and modem status information when the application
+ has set up that line status and modem status should be passed
+ back in the data stream.
+
+Arguments:
+
+ Context - Pointer to the request that is specify the escape character.
+ Implicitly - An escape character of 0 means no escaping
+ will occur.
+
+Return Value:
+
+ This routine always returns FALSE.
+
+--*/
+
+{
+
+ PREQUEST_CONTEXT reqContext = (PREQUEST_CONTEXT)Context;
+ PSERIAL_DEVICE_EXTENSION extension = SerialGetDeviceExtension(WdfInterruptGetDevice(Interrupt));
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ extension->EscapeChar = *(PUCHAR)reqContext->SystemBuffer;
+
+ return FALSE;
+}
+
+VOID
+SerialEvtIoDeviceControl(
+ IN WDFQUEUE Queue,
+ IN WDFREQUEST Request,
+ IN size_t OutputBufferLength,
+ IN size_t InputBufferLength,
+ IN ULONG IoControlCode
+ )
+
+/*++
+
+Routine Description:
+
+ This routine provides the initial processing for all of the
+ Ioctrls for the serial device.
+
+Arguments:
+
+ Request - Pointer to the WDFREQUEST for the current request
+
+Return Value:
+
+ The function value is the final status of the call
+
+--*/
+
+{
+ //
+ // The status that gets returned to the caller and
+ // set in the Request.
+ //
+ NTSTATUS Status;
+
+ //
+ // Just what it says. This is the serial specific device
+ // extension of the device object create for the serial driver.
+ //
+ PSERIAL_DEVICE_EXTENSION Extension = NULL;
+
+ PVOID buffer;
+ PREQUEST_CONTEXT reqContext;
+ size_t bufSize;
+
+ UNREFERENCED_PARAMETER(OutputBufferLength);
+ UNREFERENCED_PARAMETER(InputBufferLength);
+
+ reqContext = SerialGetRequestContext(Request);
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS, "%s for: %p\n",
+ SerialGetIoctlName(IoControlCode), Request);
+
+ Extension = SerialGetDeviceExtension(WdfIoQueueGetDevice(Queue));
+
+ //
+ // We expect to be open so all our pages are locked down. This is, after
+ // all, an IO operation, so the device should be open first.
+ //
+
+ if (Extension->DeviceIsOpened != TRUE) {
+ SerialCompleteRequest(Request, STATUS_INVALID_DEVICE_REQUEST, 0);
+ return;
+ }
+
+
+ if (SerialCompleteIfError(Extension, Request) != STATUS_SUCCESS) {
+
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS,
+ "<SerialEvtIoDeviceControl (2) %d\n", STATUS_CANCELLED);
+ return;
+
+ }
+
+ reqContext = SerialGetRequestContext(Request);
+ reqContext->Information = 0;
+ reqContext->Status = STATUS_SUCCESS;
+ reqContext->MajorFunction = IRP_MJ_DEVICE_CONTROL;
+
+
+ Status = STATUS_SUCCESS;
+
+ switch (IoControlCode) {
+
+ case IOCTL_SERIAL_SET_BAUD_RATE : {
+
+ ULONG BaudRate;
+ //
+ // Will hold the value of the appropriate divisor for
+ // the requested baud rate. If the baudrate is invalid
+ // (because the device won't support that baud rate) then
+ // this value is undefined.
+ //
+ // Note: in one sense the concept of a valid baud rate
+ // is cloudy. We could allow the user to request any
+ // baud rate. We could then calculate the divisor needed
+ // for that baud rate. As long as the divisor wasn't less
+ // than one we would be "ok". (The percentage difference
+ // between the "true" divisor and the "rounded" value given
+ // to the hardware might make it unusable, but... ) It would
+ // really be up to the user to "Know" whether the baud rate
+ // is suitable. So much for theory, *We* only support a given
+ // set of baud rates.
+ //
+ SHORT AppropriateDivisor;
+
+ Status = WdfRequestRetrieveInputBuffer (Request, sizeof(SERIAL_BAUD_RATE), &buffer, &bufSize );
+ if( !NT_SUCCESS(Status) ) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
+ break;
+ }
+
+ BaudRate = ((PSERIAL_BAUD_RATE)(buffer))->BaudRate;
+
+
+ //
+ // Get the baud rate from the request. We pass it
+ // to a routine which will set the correct divisor.
+ //
+
+ Status = SerialGetDivisorFromBaud(
+ Extension->ClockRate,
+ BaudRate,
+ &AppropriateDivisor
+ );
+
+
+ if (NT_SUCCESS(Status)) {
+
+ SERIAL_IOCTL_SYNC S;
+
+
+ Extension->CurrentBaud = BaudRate;
+ Extension->WmiCommData.BaudRate = BaudRate;
+
+ S.Extension = Extension;
+ S.Data = (PVOID) (ULONG_PTR) AppropriateDivisor;
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SerialSetBaud,
+ &S
+ );
+
+ }
+
+ break;
+ }
+
+ case IOCTL_SERIAL_GET_BAUD_RATE: {
+
+ PSERIAL_BAUD_RATE Br;
+
+ Status = WdfRequestRetrieveOutputBuffer ( Request, sizeof(SERIAL_BAUD_RATE), &buffer, &bufSize );
+ if( !NT_SUCCESS(Status) ) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
+ break;
+ }
+
+ Br = (PSERIAL_BAUD_RATE)buffer;
+
+ Br->BaudRate = Extension->CurrentBaud;
+
+ reqContext->Information = sizeof(SERIAL_BAUD_RATE);
+
+ break;
+
+ }
+
+ case IOCTL_SERIAL_GET_MODEM_CONTROL: {
+ SERIAL_IOCTL_SYNC S;
+
+ Status = WdfRequestRetrieveOutputBuffer ( Request, sizeof(ULONG), &buffer, &bufSize );
+ if( !NT_SUCCESS(Status) ) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
+ break;
+ }
+
+ reqContext->Information = sizeof(ULONG);
+
+ S.Extension = Extension;
+ S.Data = buffer;
+
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SerialGetMCRContents,
+ &S
+ );
+
+ break;
+ }
+ case IOCTL_SERIAL_SET_MODEM_CONTROL: {
+ SERIAL_IOCTL_SYNC S;
+
+ Status = WdfRequestRetrieveInputBuffer (Request, sizeof(ULONG), &buffer, &bufSize );
+ if( !NT_SUCCESS(Status) ) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
+ break;
+ }
+
+ S.Extension = Extension;
+ S.Data = buffer;
+
+
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SerialSetMCRContents,
+ &S
+ );
+
+ break;
+ }
+ case IOCTL_SERIAL_SET_FIFO_CONTROL: {
+ SERIAL_IOCTL_SYNC S;
+
+ Status = WdfRequestRetrieveInputBuffer (Request, sizeof(ULONG), &buffer, &bufSize );
+ if( !NT_SUCCESS(Status) ) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
+ break;
+ }
+
+ S.Extension = Extension;
+ S.Data = buffer;
+
+
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SerialSetFCRContents,
+ &S
+ );
+
+ break;
+ }
+ case IOCTL_SERIAL_SET_LINE_CONTROL: {
+
+ PSERIAL_LINE_CONTROL Lc;
+ UCHAR LData;
+ UCHAR LStop;
+ UCHAR LParity;
+ UCHAR Mask = 0xff;
+
+ Status = WdfRequestRetrieveInputBuffer (Request, sizeof(SERIAL_LINE_CONTROL), &buffer, &bufSize );
+ if( !NT_SUCCESS(Status) ) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
+ break;
+ }
+
+ //
+ // Points to the line control record in the Request.
+ //
+ Lc = (PSERIAL_LINE_CONTROL)buffer;
+
+ switch (Lc->WordLength) {
+ case 5: {
+
+ LData = SERIAL_5_DATA;
+ Mask = 0x1f;
+ break;
+
+ }
+ case 6: {
+
+ LData = SERIAL_6_DATA;
+ Mask = 0x3f;
+ break;
+
+ }
+ case 7: {
+
+ LData = SERIAL_7_DATA;
+ Mask = 0x7f;
+ break;
+
+ }
+ case 8: {
+
+ LData = SERIAL_8_DATA;
+ break;
+
+ }
+ default: {
+
+ Status = STATUS_INVALID_PARAMETER;
+ goto DoneWithIoctl;
+
+ }
+
+ }
+
+ Extension->WmiCommData.BitsPerByte = Lc->WordLength;
+
+ switch (Lc->Parity) {
+
+ case NO_PARITY: {
+ Extension->WmiCommData.Parity = SERIAL_WMI_PARITY_NONE;
+ LParity = SERIAL_NONE_PARITY;
+ break;
+
+ }
+ case EVEN_PARITY: {
+ Extension->WmiCommData.Parity = SERIAL_WMI_PARITY_EVEN;
+ LParity = SERIAL_EVEN_PARITY;
+ break;
+
+ }
+ case ODD_PARITY: {
+ Extension->WmiCommData.Parity = SERIAL_WMI_PARITY_ODD;
+ LParity = SERIAL_ODD_PARITY;
+ break;
+
+ }
+ case SPACE_PARITY: {
+ Extension->WmiCommData.Parity = SERIAL_WMI_PARITY_SPACE;
+ LParity = SERIAL_SPACE_PARITY;
+ break;
+
+ }
+ case MARK_PARITY: {
+ Extension->WmiCommData.Parity = SERIAL_WMI_PARITY_MARK;
+ LParity = SERIAL_MARK_PARITY;
+ break;
+
+ }
+ default: {
+
+ Status = STATUS_INVALID_PARAMETER;
+ goto DoneWithIoctl;
+ break;
+ }
+
+ }
+
+ switch (Lc->StopBits) {
+
+ case STOP_BIT_1: {
+ Extension->WmiCommData.StopBits = SERIAL_WMI_STOP_1;
+ LStop = SERIAL_1_STOP;
+ break;
+ }
+ case STOP_BITS_1_5: {
+
+ if (LData != SERIAL_5_DATA) {
+
+ Status = STATUS_INVALID_PARAMETER;
+ goto DoneWithIoctl;
+ }
+ Extension->WmiCommData.StopBits = SERIAL_WMI_STOP_1_5;
+ LStop = SERIAL_1_5_STOP;
+ break;
+
+ }
+ case STOP_BITS_2: {
+
+ if (LData == SERIAL_5_DATA) {
+
+ Status = STATUS_INVALID_PARAMETER;
+ goto DoneWithIoctl;
+ }
+ Extension->WmiCommData.StopBits = SERIAL_WMI_STOP_2;
+ LStop = SERIAL_2_STOP;
+ break;
+
+ }
+ default: {
+
+ Status = STATUS_INVALID_PARAMETER;
+ goto DoneWithIoctl;
+ }
+
+ }
+
+ Extension->LineControl =
+ (UCHAR)((Extension->LineControl & SERIAL_LCR_BREAK) |
+ (LData | LParity | LStop));
+ Extension->ValidDataMask = Mask;
+
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SerialSetLineControl,
+ Extension
+ );
+
+ break;
+ }
+ case IOCTL_SERIAL_GET_LINE_CONTROL: {
+
+ PSERIAL_LINE_CONTROL Lc;
+
+ Status = WdfRequestRetrieveOutputBuffer ( Request, sizeof(SERIAL_LINE_CONTROL), &buffer, &bufSize );
+ if( !NT_SUCCESS(Status) ) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
+ break;
+ }
+
+ Lc = (PSERIAL_LINE_CONTROL)buffer;
+
+ RtlZeroMemory(buffer, OutputBufferLength);
+
+ if ((Extension->LineControl & SERIAL_DATA_MASK) == SERIAL_5_DATA) {
+ Lc->WordLength = 5;
+ } else if ((Extension->LineControl & SERIAL_DATA_MASK)
+ == SERIAL_6_DATA) {
+ Lc->WordLength = 6;
+ } else if ((Extension->LineControl & SERIAL_DATA_MASK)
+ == SERIAL_7_DATA) {
+ Lc->WordLength = 7;
+ } else if ((Extension->LineControl & SERIAL_DATA_MASK)
+ == SERIAL_8_DATA) {
+ Lc->WordLength = 8;
+ }
+
+ if ((Extension->LineControl & SERIAL_PARITY_MASK)
+ == SERIAL_NONE_PARITY) {
+ Lc->Parity = NO_PARITY;
+ } else if ((Extension->LineControl & SERIAL_PARITY_MASK)
+ == SERIAL_ODD_PARITY) {
+ Lc->Parity = ODD_PARITY;
+ } else if ((Extension->LineControl & SERIAL_PARITY_MASK)
+ == SERIAL_EVEN_PARITY) {
+ Lc->Parity = EVEN_PARITY;
+ } else if ((Extension->LineControl & SERIAL_PARITY_MASK)
+ == SERIAL_MARK_PARITY) {
+ Lc->Parity = MARK_PARITY;
+ } else if ((Extension->LineControl & SERIAL_PARITY_MASK)
+ == SERIAL_SPACE_PARITY) {
+ Lc->Parity = SPACE_PARITY;
+ }
+
+ if (Extension->LineControl & SERIAL_2_STOP) {
+ if (Lc->WordLength == 5) {
+ Lc->StopBits = STOP_BITS_1_5;
+ } else {
+ Lc->StopBits = STOP_BITS_2;
+ }
+ } else {
+ Lc->StopBits = STOP_BIT_1;
+ }
+
+ reqContext->Information = sizeof(SERIAL_LINE_CONTROL);
+
+ break;
+ }
+ case IOCTL_SERIAL_SET_TIMEOUTS: {
+
+ PSERIAL_TIMEOUTS NewTimeouts;
+
+ Status = WdfRequestRetrieveInputBuffer ( Request, sizeof(SERIAL_TIMEOUTS), &buffer, &bufSize );
+ if( !NT_SUCCESS(Status) ) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
+ break;
+ }
+
+ NewTimeouts =(PSERIAL_TIMEOUTS)buffer;
+
+ if ((NewTimeouts->ReadIntervalTimeout == MAXULONG) &&
+ (NewTimeouts->ReadTotalTimeoutMultiplier == MAXULONG) &&
+ (NewTimeouts->ReadTotalTimeoutConstant == MAXULONG)) {
+
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+
+ }
+
+
+ Extension->Timeouts.ReadIntervalTimeout =
+ NewTimeouts->ReadIntervalTimeout;
+
+ Extension->Timeouts.ReadTotalTimeoutMultiplier =
+ NewTimeouts->ReadTotalTimeoutMultiplier;
+
+ Extension->Timeouts.ReadTotalTimeoutConstant =
+ NewTimeouts->ReadTotalTimeoutConstant;
+
+ Extension->Timeouts.WriteTotalTimeoutMultiplier =
+ NewTimeouts->WriteTotalTimeoutMultiplier;
+
+ Extension->Timeouts.WriteTotalTimeoutConstant =
+ NewTimeouts->WriteTotalTimeoutConstant;
+
+ break;
+ }
+ case IOCTL_SERIAL_GET_TIMEOUTS: {
+
+ Status = WdfRequestRetrieveOutputBuffer ( Request, sizeof(SERIAL_TIMEOUTS), &buffer, &bufSize );
+ if( !NT_SUCCESS(Status) ) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
+ break;
+ }
+
+ *((PSERIAL_TIMEOUTS)buffer) = Extension->Timeouts;
+ reqContext->Information = sizeof(SERIAL_TIMEOUTS);
+
+ break;
+ }
+ case IOCTL_SERIAL_SET_CHARS: {
+
+ SERIAL_IOCTL_SYNC S;
+ PSERIAL_CHARS NewChars;
+
+ Status = WdfRequestRetrieveInputBuffer ( Request, sizeof(SERIAL_CHARS), &buffer, &bufSize );
+ if( !NT_SUCCESS(Status) ) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
+ break;
+ }
+
+ NewChars = (PSERIAL_CHARS)buffer;
+
+ //
+ // The only thing that can be wrong with the chars
+ // is that the xon and xoff characters are the
+ // same.
+ //
+#if 0
+ if (NewChars->XonChar == NewChars->XoffChar) {
+
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+
+ }
+#endif
+
+ //
+ // We acquire the control lock so that only
+ // one request can GET or SET the characters
+ // at a time. The sets could be synchronized
+ // by the interrupt spinlock, but that wouldn't
+ // prevent multiple gets at the same time.
+ //
+
+ S.Extension = Extension;
+ S.Data = NewChars;
+
+ //
+ // Under the protection of the lock, make sure that
+ // the xon and xoff characters aren't the same as
+ // the escape character.
+ //
+
+ if (Extension->EscapeChar) {
+
+ if ((Extension->EscapeChar == NewChars->XonChar) ||
+ (Extension->EscapeChar == NewChars->XoffChar)) {
+
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+
+ }
+
+ }
+
+ Extension->WmiCommData.XonCharacter = NewChars->XonChar;
+ Extension->WmiCommData.XoffCharacter = NewChars->XoffChar;
+
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SerialSetChars,
+ &S
+ );
+
+
+ break;
+
+ }
+ case IOCTL_SERIAL_GET_CHARS: {
+
+ Status = WdfRequestRetrieveOutputBuffer ( Request, sizeof(SERIAL_CHARS), &buffer, &bufSize );
+ if( !NT_SUCCESS(Status) ) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
+ break;
+ }
+
+ *((PSERIAL_CHARS)buffer) = Extension->SpecialChars;
+ reqContext->Information = sizeof(SERIAL_CHARS);
+
+
+ break;
+ }
+ case IOCTL_SERIAL_SET_DTR:
+ case IOCTL_SERIAL_CLR_DTR: {
+
+
+ //
+ // We acquire the lock so that we can check whether
+ // automatic dtr flow control is enabled. If it is
+ // then we return an error since the app is not allowed
+ // to touch this if it is automatic.
+ //
+
+ if ((Extension->HandFlow.ControlHandShake & SERIAL_DTR_MASK)
+ == SERIAL_DTR_HANDSHAKE) {
+
+ Status = STATUS_INVALID_PARAMETER;
+
+ } else {
+
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ ((IoControlCode ==
+ IOCTL_SERIAL_SET_DTR)?
+ (SerialSetDTR):(SerialClrDTR)),
+ Extension
+ );
+
+ }
+
+ break;
+ }
+ case IOCTL_SERIAL_RESET_DEVICE: {
+
+ break;
+ }
+ case IOCTL_SERIAL_SET_RTS:
+ case IOCTL_SERIAL_CLR_RTS: {
+
+ //
+ // We acquire the lock so that we can check whether
+ // automatic rts flow control or transmit toggleing
+ // is enabled. If it is then we return an error since
+ // the app is not allowed to touch this if it is automatic
+ // or toggling.
+ //
+
+ if (((Extension->HandFlow.FlowReplace & SERIAL_RTS_MASK)
+ == SERIAL_RTS_HANDSHAKE) ||
+ ((Extension->HandFlow.FlowReplace & SERIAL_RTS_MASK)
+ == SERIAL_TRANSMIT_TOGGLE)) {
+
+ Status = STATUS_INVALID_PARAMETER;
+
+ } else {
+
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ ((IoControlCode ==
+ IOCTL_SERIAL_SET_RTS)?
+ (SerialSetRTS):(SerialClrRTS)),
+ Extension
+ );
+
+ }
+
+ break;
+
+ }
+ case IOCTL_SERIAL_SET_XOFF: {
+
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SerialPretendXoff,
+ Extension
+ );
+
+ break;
+
+ }
+ case IOCTL_SERIAL_SET_XON: {
+
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SerialPretendXon,
+ Extension
+ );
+
+ break;
+
+ }
+ case IOCTL_SERIAL_SET_BREAK_ON: {
+
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SerialTurnOnBreak,
+ Extension
+ );
+
+ break;
+ }
+ case IOCTL_SERIAL_SET_BREAK_OFF: {
+
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SerialTurnOffBreak,
+ Extension
+ );
+
+ break;
+ }
+ case IOCTL_SERIAL_SET_QUEUE_SIZE: {
+
+ //
+ // Type ahead buffer is fixed, so we just validate
+ // the the users request is not bigger that our
+ // own internal buffer size.
+ //
+
+ PSERIAL_QUEUE_SIZE Rs;
+
+ Status = WdfRequestRetrieveInputBuffer ( Request, sizeof(SERIAL_QUEUE_SIZE), &buffer, &bufSize );
+ if( !NT_SUCCESS(Status) ) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
+ break;
+ }
+
+ ASSERT(Extension->InterruptReadBuffer);
+
+ Rs = (PSERIAL_QUEUE_SIZE)buffer;
+
+ reqContext->SystemBuffer = buffer;
+
+ //
+ // We have to allocate the memory for the new
+ // buffer while we're still in the context of the
+ // caller. We don't even try to protect this
+ // with a lock because the value could be stale
+ // as soon as we release the lock - The only time
+ // we will know for sure is when we actually try
+ // to do the resize.
+ //
+
+ if (Rs->InSize <= Extension->BufferSize) {
+
+ Status = STATUS_SUCCESS;
+ break;
+
+ }
+
+ try {
+
+ reqContext->Type3InputBuffer =
+ ExAllocatePoolWithQuotaTag(
+ NonPagedPool,
+ Rs->InSize,
+ POOL_TAG
+ );
+
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+
+ reqContext->Type3InputBuffer = NULL;
+ Status = GetExceptionCode();
+
+ }
+
+ if (!reqContext->Type3InputBuffer) {
+
+ break;
+
+ }
+
+ //
+ // Well the data passed was big enough. Do the request.
+ //
+ // There are two reason we place it in the read queue:
+ //
+ // 1) We want to serialize these resize requests so that
+ // they don't contend with each other.
+ //
+ // 2) We want to serialize these requests with reads since
+ // we don't want reads and resizes contending over the
+ // read buffer.
+ //
+
+
+ SerialStartOrQueue(
+ Extension,
+ Request,
+ Extension->ReadQueue,
+ &Extension->CurrentReadRequest,
+ SerialStartRead
+ );
+
+ return;
+ }
+ case IOCTL_SERIAL_GET_WAIT_MASK: {
+
+ Status = WdfRequestRetrieveOutputBuffer ( Request, sizeof(ULONG), &buffer, &bufSize );
+ if( !NT_SUCCESS(Status) ) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
+ break;
+ }
+
+ //
+ // Simple scalar read. No reason to acquire a lock.
+ //
+
+ reqContext->Information = sizeof(ULONG);
+
+ *((ULONG *)buffer) = Extension->IsrWaitMask;
+
+ break;
+
+ }
+ case IOCTL_SERIAL_SET_WAIT_MASK: {
+
+ ULONG NewMask;
+
+ SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "In Ioctl processing for set mask\n");
+
+ Status = WdfRequestRetrieveInputBuffer ( Request, sizeof(ULONG), &buffer, &bufSize );
+ if( !NT_SUCCESS(Status) ) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
+ break;
+ }
+
+ NewMask = *((ULONG *)buffer);
+ reqContext->SystemBuffer = buffer;
+
+ //
+ // Make sure that the mask only contains valid
+ // waitable events.
+ //
+
+ if (NewMask & ~(SERIAL_EV_RXCHAR |
+ SERIAL_EV_RXFLAG |
+ SERIAL_EV_TXEMPTY |
+ SERIAL_EV_CTS |
+ SERIAL_EV_DSR |
+ SERIAL_EV_RLSD |
+ SERIAL_EV_BREAK |
+ SERIAL_EV_ERR |
+ SERIAL_EV_RING |
+ SERIAL_EV_PERR |
+ SERIAL_EV_RX80FULL |
+ SERIAL_EV_EVENT1 |
+ SERIAL_EV_EVENT2)) {
+
+ SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Unknown mask %x\n", NewMask);
+
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+
+ }
+
+ //
+ // Either start this request or put it on the
+ // queue.
+ //
+
+ SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Starting or queuing set mask request %p"
+ "\n", Request);
+
+ SerialStartOrQueue(Extension, Request, Extension->MaskQueue,
+ &Extension->CurrentMaskRequest,
+ SerialStartMask);
+ return;
+
+ }
+ case IOCTL_SERIAL_WAIT_ON_MASK: {
+
+ SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "In Ioctl processing for wait mask\n");
+
+ Status = WdfRequestRetrieveOutputBuffer ( Request, sizeof(ULONG), &buffer, &bufSize );
+ if( !NT_SUCCESS(Status) ) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
+ break;
+ }
+
+ reqContext->SystemBuffer = buffer;
+
+ //
+ // Either start this request or put it on the
+ // queue.
+ //
+
+ SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Starting or queuing wait mask request"
+ "%p\n", Request);
+
+ SerialStartOrQueue(
+ Extension,
+ Request,
+ Extension->MaskQueue,
+ &Extension->CurrentMaskRequest,
+ SerialStartMask
+ );
+ return;
+ }
+ case IOCTL_SERIAL_IMMEDIATE_CHAR: {
+
+ Status = WdfRequestRetrieveInputBuffer ( Request, sizeof(UCHAR), &buffer, &bufSize );
+ if( !NT_SUCCESS(Status) ) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
+ break;
+ }
+
+ reqContext->SystemBuffer = buffer;
+
+ if (Extension->CurrentImmediateRequest) {
+
+ Status = STATUS_INVALID_PARAMETER;
+
+ } else {
+
+ //
+ // We can queue the char. We need to set
+ // a cancel routine because flow control could
+ // keep the char from transmitting. Make sure
+ // that the request hasn't already been canceled.
+ //
+
+ Extension->CurrentImmediateRequest = Request;
+ Extension->TotalCharsQueued++;
+ SerialStartImmediate(Extension);
+ return;
+
+ }
+
+ break;
+
+ }
+ case IOCTL_SERIAL_PURGE: {
+
+ ULONG Mask;
+
+ Status = WdfRequestRetrieveInputBuffer ( Request, sizeof(ULONG), &buffer, &bufSize );
+ if( !NT_SUCCESS(Status) ) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
+ break;
+ }
+
+ //
+ // Check to make sure that the mask only has
+ // 0 or the other appropriate values.
+ //
+
+ Mask = *((ULONG *)(buffer));
+
+ if ((!Mask) || (Mask & (~(SERIAL_PURGE_TXABORT |
+ SERIAL_PURGE_RXABORT |
+ SERIAL_PURGE_TXCLEAR |
+ SERIAL_PURGE_RXCLEAR
+ )
+ )
+ )) {
+
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+
+ }
+
+ reqContext->SystemBuffer = buffer;
+
+ //
+ // Either start this request or put it on the
+ // queue.
+ //
+
+ SerialStartOrQueue(
+ Extension,
+ Request,
+ Extension->PurgeQueue,
+ &Extension->CurrentPurgeRequest,
+ SerialStartPurge
+ );
+ return;
+ }
+ case IOCTL_SERIAL_GET_HANDFLOW: {
+
+ Status = WdfRequestRetrieveOutputBuffer ( Request, sizeof(SERIAL_HANDFLOW), &buffer, &bufSize );
+ if( !NT_SUCCESS(Status) ) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
+ break;
+ }
+
+ reqContext->Information = sizeof(SERIAL_HANDFLOW);
+
+ *((PSERIAL_HANDFLOW)buffer) = Extension->HandFlow;
+
+ break;
+
+ }
+ case IOCTL_SERIAL_SET_HANDFLOW: {
+
+ SERIAL_IOCTL_SYNC S;
+ PSERIAL_HANDFLOW HandFlow;
+
+ //
+ // Make sure that the hand shake and control is the
+ // right size.
+ //
+
+ Status = WdfRequestRetrieveInputBuffer ( Request, sizeof(SERIAL_HANDFLOW), &buffer, &bufSize );
+ if( !NT_SUCCESS(Status) ) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
+ break;
+ }
+
+ HandFlow = (PSERIAL_HANDFLOW)buffer;
+
+ //
+ // Make sure that there are no invalid bits set in
+ // the control and handshake.
+ //
+
+ if (HandFlow->ControlHandShake & SERIAL_CONTROL_INVALID) {
+
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+
+ }
+
+ if (HandFlow->FlowReplace & SERIAL_FLOW_INVALID) {
+
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+
+ }
+
+ //
+ // Make sure that the app hasn't set an invlid DTR mode.
+ //
+
+ if ((HandFlow->ControlHandShake & SERIAL_DTR_MASK) ==
+ SERIAL_DTR_MASK) {
+
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+
+ }
+
+ //
+ // Make sure that haven't set totally invalid xon/xoff
+ // limits.
+ //
+
+ if ((HandFlow->XonLimit < 0) ||
+ ((ULONG)HandFlow->XonLimit > Extension->BufferSize)) {
+
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+
+ }
+
+ if ((HandFlow->XoffLimit < 0) ||
+ ((ULONG)HandFlow->XoffLimit > Extension->BufferSize)) {
+
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+
+ }
+
+ S.Extension = Extension;
+ S.Data = HandFlow;
+
+ //
+ // Under the protection of the lock, make sure that
+ // we aren't turning on error replacement when we
+ // are doing line status/modem status insertion.
+ //
+
+ if (Extension->EscapeChar) {
+
+ if (HandFlow->FlowReplace & SERIAL_ERROR_CHAR) {
+
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+
+ }
+
+ }
+
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SerialSetHandFlow,
+ &S
+ );
+
+ break;
+
+ }
+ case IOCTL_SERIAL_GET_MODEMSTATUS: {
+
+ SERIAL_IOCTL_SYNC S;
+
+ Status = WdfRequestRetrieveOutputBuffer ( Request, sizeof(ULONG), &buffer, &bufSize );
+ if( !NT_SUCCESS(Status) ) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
+ break;
+ }
+
+ reqContext->Information = sizeof(ULONG);
+
+ S.Extension = Extension;
+ S.Data = buffer;
+
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SerialGetModemUpdate,
+ &S
+ );
+
+ break;
+
+ }
+ case IOCTL_SERIAL_GET_DTRRTS: {
+
+ ULONG ModemControl;
+
+ Status = WdfRequestRetrieveOutputBuffer ( Request, sizeof(ULONG), &buffer, &bufSize );
+ if( !NT_SUCCESS(Status) ) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
+ break;
+ }
+
+ reqContext->Information = sizeof(ULONG);
+ reqContext->Status = STATUS_SUCCESS;
+
+ //
+ // Reading this hardware has no effect on the device.
+ //
+
+ ModemControl = READ_MODEM_CONTROL(Extension, Extension->Controller);
+
+ ModemControl &= SERIAL_DTR_STATE | SERIAL_RTS_STATE;
+
+ *(PULONG)buffer = ModemControl;
+
+ break;
+
+ }
+ case IOCTL_SERIAL_GET_COMMSTATUS: {
+
+ SERIAL_IOCTL_SYNC S;
+
+ Status = WdfRequestRetrieveOutputBuffer ( Request, sizeof(SERIAL_STATUS), &buffer, &bufSize );
+ if( !NT_SUCCESS(Status) ) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
+ break;
+ }
+
+ reqContext->Information = sizeof(SERIAL_STATUS);
+
+ S.Extension = Extension;
+ S.Data = buffer;
+
+ //
+ // Acquire the cancel spin lock so nothing much
+ // changes while were getting the state.
+ //
+
+ //IoAcquireCancelSpinLock(&OldIrql);
+
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SerialGetCommStatus,
+ &S
+ );
+
+ //IoReleaseCancelSpinLock(OldIrql);
+
+ break;
+
+ }
+ case IOCTL_SERIAL_GET_PROPERTIES: {
+
+
+ Status = WdfRequestRetrieveOutputBuffer ( Request, sizeof(SERIAL_COMMPROP), &buffer, &bufSize );
+ if( !NT_SUCCESS(Status) ) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
+ break;
+ }
+
+ //
+ // No synchronization is required since this information
+ // is "static".
+ //
+
+ SerialGetProperties(
+ Extension,
+ buffer
+ );
+
+ reqContext->Information = sizeof(SERIAL_COMMPROP);
+ reqContext->Status = STATUS_SUCCESS;
+
+ break;
+ }
+ case IOCTL_SERIAL_XOFF_COUNTER: {
+
+ PSERIAL_XOFF_COUNTER Xc;
+
+ Status = WdfRequestRetrieveInputBuffer ( Request, sizeof(SERIAL_XOFF_COUNTER), &buffer, &bufSize );
+ if( !NT_SUCCESS(Status) ) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
+ break;
+ }
+
+ Xc = (PSERIAL_XOFF_COUNTER)buffer;
+
+ if (Xc->Counter <= 0) {
+
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+
+ }
+ reqContext->SystemBuffer = buffer;
+
+ //
+ // There is no output, so make that clear now
+ //
+
+ reqContext->Information = 0;
+
+ //
+ // So far so good. Put the request onto the write queue.
+ //
+
+ SerialStartOrQueue(
+ Extension,
+ Request,
+ Extension->WriteQueue,
+ &Extension->CurrentWriteRequest,
+ SerialStartWrite
+ );
+ return;
+
+ }
+ case IOCTL_SERIAL_LSRMST_INSERT: {
+
+ PUCHAR escapeChar;
+ SERIAL_IOCTL_SYNC S;
+
+ //
+ // Make sure we get a byte.
+ //
+ Status = WdfRequestRetrieveInputBuffer ( Request, sizeof(UCHAR), &buffer, &bufSize );
+ if( !NT_SUCCESS(Status) ) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
+ break;
+ }
+
+ reqContext->SystemBuffer = buffer;
+
+ escapeChar = (PUCHAR)buffer;
+
+ if (*escapeChar) {
+
+ //
+ // We've got some escape work to do. We will make sure that
+ // the character is not the same as the Xon or Xoff character,
+ // or that we are already doing error replacement.
+ //
+
+ if ((*escapeChar == Extension->SpecialChars.XoffChar) ||
+ (*escapeChar == Extension->SpecialChars.XonChar) ||
+ (Extension->HandFlow.FlowReplace & SERIAL_ERROR_CHAR)) {
+
+ Status = STATUS_INVALID_PARAMETER;
+
+ break;
+
+ }
+
+ }
+
+ S.Extension = Extension;
+ S.Data = buffer;
+
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SerialSetEscapeChar,
+ reqContext
+ );
+
+ break;
+
+ }
+ case IOCTL_SERIAL_CONFIG_SIZE: {
+
+ Status = WdfRequestRetrieveOutputBuffer ( Request, sizeof(ULONG), &buffer, &bufSize );
+ if( !NT_SUCCESS(Status) ) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
+ break;
+ }
+
+ reqContext->Information = sizeof(ULONG);
+ reqContext->Status = STATUS_SUCCESS;
+
+ *(PULONG)buffer = 0;
+
+ break;
+ }
+ case IOCTL_SERIAL_GET_STATS: {
+
+ Status = WdfRequestRetrieveOutputBuffer ( Request, sizeof(SERIALPERF_STATS), &buffer, &bufSize );
+ if( !NT_SUCCESS(Status) ) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", Status);
+ break;
+ }
+
+ reqContext->SystemBuffer = buffer;
+
+ reqContext->Information = sizeof(SERIALPERF_STATS);
+ reqContext->Status = STATUS_SUCCESS;
+
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SerialGetStats,
+ reqContext
+ );
+
+ break;
+ }
+ case IOCTL_SERIAL_CLEAR_STATS: {
+
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SerialClearStats,
+ Extension
+ );
+ break;
+ }
+ default: {
+
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+ }
+
+DoneWithIoctl:;
+
+ reqContext->Status = Status;
+
+ SerialCompleteRequest(Request, Status, reqContext->Information);
+
+ return;
+
+}
+
+
+VOID
+SerialGetProperties(
+ IN PSERIAL_DEVICE_EXTENSION Extension,
+ IN PSERIAL_COMMPROP Properties
+ )
+
+/*++
+
+Routine Description:
+
+ This function returns the capabilities of this particular
+ serial device.
+
+Arguments:
+
+ Extension - The serial device extension.
+
+ Properties - The structure used to return the properties
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+
+ RtlZeroMemory(
+ Properties,
+ sizeof(SERIAL_COMMPROP)
+ );
+
+ Properties->PacketLength = sizeof(SERIAL_COMMPROP);
+ Properties->PacketVersion = 2;
+ Properties->ServiceMask = SERIAL_SP_SERIALCOMM;
+ Properties->MaxTxQueue = 0;
+ Properties->MaxRxQueue = 0;
+
+ Properties->MaxBaud = SERIAL_BAUD_USER;
+ Properties->SettableBaud = Extension->SupportedBauds;
+
+ Properties->ProvSubType = SERIAL_SP_RS232;
+ Properties->ProvCapabilities = SERIAL_PCF_DTRDSR |
+ SERIAL_PCF_RTSCTS |
+ SERIAL_PCF_CD |
+ SERIAL_PCF_PARITY_CHECK |
+ SERIAL_PCF_XONXOFF |
+ SERIAL_PCF_SETXCHAR |
+ SERIAL_PCF_TOTALTIMEOUTS |
+ SERIAL_PCF_INTTIMEOUTS;
+ Properties->SettableParams = SERIAL_SP_PARITY |
+ SERIAL_SP_BAUD |
+ SERIAL_SP_DATABITS |
+ SERIAL_SP_STOPBITS |
+ SERIAL_SP_HANDSHAKING |
+ SERIAL_SP_PARITY_CHECK |
+ SERIAL_SP_CARRIER_DETECT;
+
+
+ Properties->SettableData = SERIAL_DATABITS_5 |
+ SERIAL_DATABITS_6 |
+ SERIAL_DATABITS_7 |
+ SERIAL_DATABITS_8;
+ Properties->SettableStopParity = SERIAL_STOPBITS_10 |
+ SERIAL_STOPBITS_15 |
+ SERIAL_STOPBITS_20 |
+ SERIAL_PARITY_NONE |
+ SERIAL_PARITY_ODD |
+ SERIAL_PARITY_EVEN |
+ SERIAL_PARITY_MARK |
+ SERIAL_PARITY_SPACE;
+ Properties->CurrentTxQueue = 0;
+ Properties->CurrentRxQueue = Extension->BufferSize;
+
+}
+
+VOID
+SerialEvtIoInternalDeviceControl(
+ IN WDFQUEUE Queue,
+ IN WDFREQUEST Request,
+ IN size_t OutputBufferLength,
+ IN size_t InputBufferLength,
+ IN ULONG IoControlCode
+)
+/*++
+
+Routine Description:
+
+ This routine provides the initial processing for all of the
+ internal Ioctrls for the serial device.
+
+Arguments:
+
+ PDevObj - Pointer to the device object for this device
+
+ PIrp - Pointer to the WDFREQUEST for the current request
+
+Return Value:
+
+ The function value is the final status of the call
+
+--*/
+
+{
+ NTSTATUS status;
+ PSERIAL_DEVICE_EXTENSION pDevExt = NULL;
+ PVOID buffer;
+ PREQUEST_CONTEXT reqContext;
+ WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS wakeSettings;
+ size_t bufSize;
+
+ UNREFERENCED_PARAMETER(OutputBufferLength);
+ UNREFERENCED_PARAMETER(InputBufferLength);
+
+ SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "SerialEvtIoInternalDeviceControl for: %p\n", Request);
+
+ pDevExt = SerialGetDeviceExtension(WdfIoQueueGetDevice(Queue));
+
+ if (SerialCompleteIfError(pDevExt, Request) != STATUS_SUCCESS) {
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS,
+ "<SerialEvtIoDeviceControl (2) %d\n", STATUS_CANCELLED);
+ return;
+
+ }
+
+ reqContext = SerialGetRequestContext(Request);
+ reqContext->Information = 0;
+ reqContext->Status = STATUS_SUCCESS;
+ reqContext->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
+
+ switch (IoControlCode) {
+
+ case IOCTL_SERIAL_INTERNAL_DO_WAIT_WAKE:
+ //
+ // Init wait-wake policy structure.
+ //
+ WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS_INIT(&wakeSettings);
+ //
+ // Override the default settings from allow user control to do not allow.
+ //
+ wakeSettings.UserControlOfWakeSettings = IdleDoNotAllowUserControl;
+ status = WdfDeviceAssignSxWakeSettings(pDevExt->WdfDevice, &wakeSettings);
+ if (!NT_SUCCESS(status)) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, "WdfDeviceAssignSxWakeSettings failed %x \n", status);
+ break;
+ }
+
+ pDevExt->IsWakeEnabled = TRUE;
+ status = STATUS_SUCCESS;
+ break;
+
+ case IOCTL_SERIAL_INTERNAL_CANCEL_WAIT_WAKE:
+
+ WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS_INIT(&wakeSettings);
+ //
+ // Override the default settings.
+ //
+ wakeSettings.Enabled = WdfFalse; // Disables wait-wake
+ wakeSettings.UserControlOfWakeSettings = IdleDoNotAllowUserControl;
+ status = WdfDeviceAssignSxWakeSettings(pDevExt->WdfDevice, &wakeSettings);
+ if (!NT_SUCCESS(status)) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, "WdfDeviceAssignSxWakeSettings failed %x \n", status);
+ break;
+ }
+
+ pDevExt->IsWakeEnabled = FALSE;
+ status = STATUS_SUCCESS;
+ break;
+
+
+ //
+ // Put the serial port in a "filter-driver" appropriate state
+ //
+ // WARNING: This code assumes it is being called by a trusted kernel
+ // entity and no checking is done on the validity of the settings
+ // passed to IOCTL_SERIAL_INTERNAL_RESTORE_SETTINGS
+ //
+ // If validity checking is desired, the regular ioctl's should be used
+ //
+
+ case IOCTL_SERIAL_INTERNAL_BASIC_SETTINGS:
+ case IOCTL_SERIAL_INTERNAL_RESTORE_SETTINGS: {
+
+ SERIAL_BASIC_SETTINGS basic;
+ PSERIAL_BASIC_SETTINGS pBasic;
+ SERIAL_IOCTL_SYNC S;
+
+ if (IoControlCode == IOCTL_SERIAL_INTERNAL_BASIC_SETTINGS) {
+
+
+ //
+ // Check the buffer size
+ //
+ status = WdfRequestRetrieveOutputBuffer ( Request, sizeof(SERIAL_BASIC_SETTINGS), &buffer, &bufSize );
+ if( !NT_SUCCESS(status) ) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", status);
+ break;
+ }
+
+ reqContext->SystemBuffer = buffer;
+
+ //
+ // Everything is 0 -- timeouts and flow control and fifos. If
+ // We add additional features, this zero memory method
+ // may not work.
+ //
+
+ RtlZeroMemory(&basic, sizeof(SERIAL_BASIC_SETTINGS));
+
+ basic.TxFifo = 1;
+ basic.RxFifo = SERIAL_1_BYTE_HIGH_WATER;
+
+ reqContext->Information = sizeof(SERIAL_BASIC_SETTINGS);
+ pBasic = (PSERIAL_BASIC_SETTINGS)buffer;
+
+ //
+ // Save off the old settings
+ //
+
+ RtlCopyMemory(&pBasic->Timeouts, &pDevExt->Timeouts,
+ sizeof(SERIAL_TIMEOUTS));
+
+ RtlCopyMemory(&pBasic->HandFlow, &pDevExt->HandFlow,
+ sizeof(SERIAL_HANDFLOW));
+
+ pBasic->RxFifo = pDevExt->RxFifoTrigger;
+ pBasic->TxFifo = pDevExt->TxFifoAmount;
+
+ //
+ // Point to our new settings
+ //
+
+ pBasic = &basic;
+ } else { // restoring settings
+
+ status = WdfRequestRetrieveInputBuffer ( Request, sizeof(SERIAL_BASIC_SETTINGS), &buffer, &bufSize );
+ if( !NT_SUCCESS(status) ) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_IOCTLS, "Could not get request memory buffer %X\n", status);
+ break;
+ }
+
+ pBasic = (PSERIAL_BASIC_SETTINGS)buffer;
+ }
+
+ //
+ // Set the timeouts
+ //
+
+ RtlCopyMemory(&pDevExt->Timeouts, &pBasic->Timeouts,
+ sizeof(SERIAL_TIMEOUTS));
+
+ //
+ // Set flowcontrol
+ //
+
+ S.Extension = pDevExt;
+ S.Data = &pBasic->HandFlow;
+ WdfInterruptSynchronize(pDevExt->WdfInterrupt, SerialSetHandFlow, &S);
+
+ if (pDevExt->FifoPresent) {
+ pDevExt->TxFifoAmount = pBasic->TxFifo;
+ pDevExt->RxFifoTrigger = (UCHAR)pBasic->RxFifo;
+
+ WRITE_FIFO_CONTROL(pDevExt, pDevExt->Controller, (UCHAR)0);
+ READ_RECEIVE_BUFFER(pDevExt, pDevExt->Controller);
+ WRITE_FIFO_CONTROL(pDevExt, pDevExt->Controller,
+ (UCHAR)(SERIAL_FCR_ENABLE | pDevExt->RxFifoTrigger
+ | SERIAL_FCR_RCVR_RESET
+ | SERIAL_FCR_TXMT_RESET));
+ } else {
+ pDevExt->TxFifoAmount = pDevExt->RxFifoTrigger = 0;
+ WRITE_FIFO_CONTROL(pDevExt, pDevExt->Controller, (UCHAR)0);
+ }
+
+
+ break;
+ }
+
+ default:
+ status = STATUS_INVALID_PARAMETER;
+ break;
+
+ }
+
+ reqContext->Status = status;
+
+ SerialCompleteRequest(Request, reqContext->Status, reqContext->Information);
+
+ return;
+}
+
+
+
diff --git a/serial/serial/isr.c b/serial/serial/isr.c
new file mode 100644
index 00000000..e76ba29b
--- /dev/null
+++ b/serial/serial/isr.c
@@ -0,0 +1,1517 @@
+/*++
+
+Copyright (c) Microsoft Corporation
+
+Module Name:
+
+ isr.c
+
+Abstract:
+
+ This module contains the interrupt service routine for the
+ serial driver.
+
+Environment:
+
+ Kernel mode
+
+--*/
+
+#include "precomp.h"
+
+#if defined(EVENT_TRACING)
+#include "isr.tmh"
+#endif
+
+
+NTSTATUS
+SerialEvtInterruptEnable(
+ IN WDFINTERRUPT Interrupt,
+ IN WDFDEVICE AssociatedDevice
+ )
+/*++
+
+Routine Description:
+
+ This event is called when the Framework moves the device to D0, and after
+ EvtDeviceD0Entry. The driver should enable its interrupt here.
+
+ This function will be called at the device's assigned interrupt
+ IRQL (DIRQL.)
+
+Arguments:
+
+ Interrupt - Handle to a Framework interrupt object.
+
+ AssociatedDevice - Handle to a Framework device object.
+
+Return Value:
+
+ BOOLEAN - TRUE indicates that the interrupt was successfully enabled.
+
+--*/
+{
+ UNREFERENCED_PARAMETER(Interrupt);
+ UNREFERENCED_PARAMETER(AssociatedDevice);
+
+ SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_PNP, "--> SerialEvtInterruptEnable\n");
+
+ SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_PNP, "<-- SerialEvtInterruptEnable\n");
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+SerialEvtInterruptDisable(
+ IN WDFINTERRUPT Interrupt,
+ IN WDFDEVICE AssociatedDevice
+ )
+/*++
+
+Routine Description:
+
+ This event is called before the Framework moves the device to D1, D2 or D3
+ and before EvtDeviceD0Exit. The driver should disable its interrupt here.
+
+ This function will be called at the device's assigned interrupt
+ IRQL (DIRQL.)
+
+Arguments:
+
+ Interrupt - Handle to a Framework interrupt object.
+
+ AssociatedDevice - Handle to a Framework device object.
+
+Return Value:
+
+ BOOLEAN - TRUE indicates that the interrupt was successfully disabled.
+
+--*/
+{
+ UNREFERENCED_PARAMETER(Interrupt);
+ UNREFERENCED_PARAMETER(AssociatedDevice);
+
+ SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_PNP, "--> SerialEvtInterruptDisable\n");
+
+ SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_PNP, "<-- SerialEvtInterruptDisable\n");
+
+ return STATUS_SUCCESS;
+}
+
+BOOLEAN
+SerialISR(
+ IN WDFINTERRUPT Interrupt,
+ IN ULONG MessageID
+ )
+
+/*++
+
+Routine Description:
+
+ This is the interrupt service routine for the serial port driver.
+ It will determine whether the serial port is the source of this
+ interrupt. If it is, then this routine will do the minimum of
+ processing to quiet the interrupt. It will store any information
+ necessary for later processing.
+
+Arguments:
+
+ InterruptObject - Points to the interrupt object declared for this
+ device. We *do not* use this parameter.
+
+
+Return Value:
+
+ This function will return TRUE if the serial port is the source
+ of this interrupt, FALSE otherwise.
+
+--*/
+
+{
+ //
+ // Holds the information specific to handling this device.
+ //
+ PSERIAL_DEVICE_EXTENSION Extension = NULL;
+
+ //
+ // Holds the contents of the interrupt identification record.
+ // A low bit of zero in this register indicates that there is
+ // an interrupt pending on this device.
+ //
+ UCHAR InterruptIdReg;
+
+ //
+ // Will hold whether we've serviced any interrupt causes in this
+ // routine.
+ //
+ BOOLEAN ServicedAnInterrupt;
+
+ UCHAR tempLSR;
+ PREQUEST_CONTEXT reqContext = NULL;
+
+ UNREFERENCED_PARAMETER(MessageID);
+
+ Extension = SerialGetDeviceExtension(WdfInterruptGetDevice(Interrupt));
+
+ //
+ // Make sure we have an interrupt pending. If we do then
+ // we need to make sure that the device is open. If the
+ // device isn't open or powered down then quiet the device. Note that
+ // if the device isn't opened when we enter this routine
+ // it can't open while we're in it.
+ //
+
+ InterruptIdReg = READ_INTERRUPT_ID_REG(Extension, Extension->Controller);
+
+ if ((InterruptIdReg & SERIAL_IIR_NO_INTERRUPT_PENDING)) {
+
+ ServicedAnInterrupt = FALSE;
+
+ } else if (!Extension->DeviceIsOpened/*
+ || (Extension->PowerState != PowerDeviceD0)*/) {
+
+
+ //
+ // We got an interrupt with the device being closed or when the
+ // device is supposed to be powered down. This
+ // is not unlikely with a serial device. We just quietly
+ // keep servicing the causes until it calms down.
+ //
+
+ ServicedAnInterrupt = TRUE;
+ do {
+
+ InterruptIdReg &= (~SERIAL_IIR_FIFOS_ENABLED);
+ switch (InterruptIdReg) {
+
+ case SERIAL_IIR_RLS: {
+
+ READ_LINE_STATUS(Extension, Extension->Controller);
+
+ break;
+
+ }
+
+ case SERIAL_IIR_RDA:
+ case SERIAL_IIR_CTI: {
+
+ READ_RECEIVE_BUFFER(Extension, Extension->Controller);
+
+ break;
+
+ }
+
+ case SERIAL_IIR_THR: {
+
+ //
+ // Alread clear from reading the iir.
+ //
+ // We want to keep close track of whether
+ // the holding register is empty.
+ //
+
+ Extension->HoldingEmpty = TRUE;
+ break;
+
+ }
+
+ case SERIAL_IIR_MS: {
+
+ READ_MODEM_STATUS(Extension, Extension->Controller);
+ break;
+
+ }
+
+ default: {
+
+ ASSERT(FALSE);
+ break;
+
+ }
+
+ }
+
+ } while (!((InterruptIdReg =
+ READ_INTERRUPT_ID_REG(Extension, Extension->Controller))
+ & SERIAL_IIR_NO_INTERRUPT_PENDING));
+
+ } else {
+
+ ServicedAnInterrupt = TRUE;
+ do {
+
+ //
+ // We only care about bits that can denote an interrupt.
+ //
+
+ InterruptIdReg &= SERIAL_IIR_RLS | SERIAL_IIR_RDA |
+ SERIAL_IIR_CTI | SERIAL_IIR_THR |
+ SERIAL_IIR_MS;
+
+ //
+ // We have an interrupt. We look for interrupt causes
+ // in priority order. The presence of a higher interrupt
+ // will mask out causes of a lower priority. When we service
+ // and quiet a higher priority interrupt we then need to check
+ // the interrupt causes to see if a new interrupt cause is
+ // present.
+ //
+
+ switch (InterruptIdReg) {
+
+ case SERIAL_IIR_RLS: {
+
+ SerialProcessLSR(Extension);
+
+ break;
+
+ }
+
+ case SERIAL_IIR_RDA:
+ case SERIAL_IIR_CTI:
+
+ {
+
+ //
+ // Reading the receive buffer will quiet this interrupt.
+ //
+ // It may also reveal a new interrupt cause.
+ //
+ UCHAR ReceivedChar;
+
+ do {
+
+ ReceivedChar =
+ READ_RECEIVE_BUFFER(Extension, Extension->Controller);
+ Extension->PerfStats.ReceivedCount++;
+ Extension->WmiPerfData.ReceivedCount++;
+
+ ReceivedChar &= Extension->ValidDataMask;
+
+ if (!ReceivedChar &&
+ (Extension->HandFlow.FlowReplace &
+ SERIAL_NULL_STRIPPING)) {
+
+ //
+ // If what we got is a null character
+ // and we're doing null stripping, then
+ // we simply act as if we didn't see it.
+ //
+
+ goto ReceiveDoLineStatus;
+
+ }
+
+ if ((Extension->HandFlow.FlowReplace &
+ SERIAL_AUTO_TRANSMIT) &&
+ ((ReceivedChar ==
+ Extension->SpecialChars.XonChar) ||
+ (ReceivedChar ==
+ Extension->SpecialChars.XoffChar))) {
+
+ //
+ // No matter what happens this character
+ // will never get seen by the app.
+ //
+
+ if (ReceivedChar ==
+ Extension->SpecialChars.XoffChar) {
+
+ Extension->TXHolding |= SERIAL_TX_XOFF;
+
+ if ((Extension->HandFlow.FlowReplace &
+ SERIAL_RTS_MASK) ==
+ SERIAL_TRANSMIT_TOGGLE) {
+
+ SerialInsertQueueDpc(
+ Extension->StartTimerLowerRTSDpc
+ )?Extension->CountOfTryingToLowerRTS++:0;
+
+ }
+
+
+ } else {
+
+ if (Extension->TXHolding & SERIAL_TX_XOFF) {
+
+ //
+ // We got the xon char **AND*** we
+ // were being held up on transmission
+ // by xoff. Clear that we are holding
+ // due to xoff. Transmission will
+ // automatically restart because of
+ // the code outside the main loop that
+ // catches problems chips like the
+ // SMC and the Winbond.
+ //
+
+ Extension->TXHolding &= ~SERIAL_TX_XOFF;
+
+ }
+
+ }
+
+ goto ReceiveDoLineStatus;
+
+ }
+
+ //
+ // Check to see if we should note
+ // the receive character or special
+ // character event.
+ //
+
+ if (Extension->IsrWaitMask) {
+
+ if (Extension->IsrWaitMask &
+ SERIAL_EV_RXCHAR) {
+
+ Extension->HistoryMask |= SERIAL_EV_RXCHAR;
+
+ }
+
+ if ((Extension->IsrWaitMask &
+ SERIAL_EV_RXFLAG) &&
+ (Extension->SpecialChars.EventChar ==
+ ReceivedChar)) {
+
+ Extension->HistoryMask |= SERIAL_EV_RXFLAG;
+
+ }
+
+ if (Extension->IrpMaskLocation &&
+ Extension->HistoryMask) {
+
+ *Extension->IrpMaskLocation =
+ Extension->HistoryMask;
+ Extension->IrpMaskLocation = NULL;
+ Extension->HistoryMask = 0;
+ reqContext = SerialGetRequestContext(Extension->CurrentWaitRequest);
+ reqContext->Information = sizeof(ULONG);
+ SerialInsertQueueDpc(
+ Extension->CommWaitDpc
+ );
+
+ }
+
+ }
+
+ SerialPutChar(
+ Extension,
+ ReceivedChar
+ );
+
+ //
+ // If we're doing line status and modem
+ // status insertion then we need to insert
+ // a zero following the character we just
+ // placed into the buffer to mark that this
+ // was reception of what we are using to
+ // escape.
+ //
+
+ if (Extension->EscapeChar &&
+ (Extension->EscapeChar ==
+ ReceivedChar)) {
+
+ SerialPutChar(
+ Extension,
+ SERIAL_LSRMST_ESCAPE
+ );
+
+ }
+
+
+ReceiveDoLineStatus: ;
+ //
+ // This reads the interrupt ID register and detemines if bits are 0
+ // If either of the reserved bits are 1, we stop servicing interrupts
+ // Since this detection method is not guarenteed this is enabled via
+ // a registry entry "UartDetectRemoval" and intialized on DriverEntry.
+ // This is disabled by default and will only be enabled on Stratus systems
+ // that allow hot replacement of serial cards
+ //
+ if(Extension->UartRemovalDetect)
+ {
+ UCHAR DetectRemoval;
+
+ DetectRemoval = READ_INTERRUPT_ID_REG(Extension, Extension->Controller);
+
+ if(DetectRemoval & SERIAL_IIR_MUST_BE_ZERO)
+ {
+ // break out of this loop and stop processing interrupts
+ break;
+ }
+ }
+
+ if (!((tempLSR = SerialProcessLSR(Extension)) &
+ SERIAL_LSR_DR)) {
+
+ //
+ // No more characters, get out of the
+ // loop.
+ //
+
+ break;
+
+ }
+
+ if ((tempLSR & ~(SERIAL_LSR_THRE | SERIAL_LSR_TEMT |
+ SERIAL_LSR_DR)) &&
+ Extension->EscapeChar) {
+
+ //
+ // An error was indicated and inserted into the
+ // stream, get out of the loop.
+ //
+
+ break;
+ }
+
+ } WHILE (TRUE);
+
+ break;
+
+ }
+
+ case SERIAL_IIR_THR: {
+
+doTrasmitStuff:;
+ Extension->HoldingEmpty = TRUE;
+
+ if (Extension->WriteLength ||
+ Extension->TransmitImmediate ||
+ Extension->SendXoffChar ||
+ Extension->SendXonChar) {
+
+ //
+ // Even though all of the characters being
+ // sent haven't all been sent, this variable
+ // will be checked when the transmit queue is
+ // empty. If it is still true and there is a
+ // wait on the transmit queue being empty then
+ // we know we finished transmitting all characters
+ // following the initiation of the wait since
+ // the code that initiates the wait will set
+ // this variable to false.
+ //
+ // One reason it could be false is that
+ // the writes were cancelled before they
+ // actually started, or that the writes
+ // failed due to timeouts. This variable
+ // basically says a character was written
+ // by the isr at some point following the
+ // initiation of the wait.
+ //
+
+ Extension->EmptiedTransmit = TRUE;
+
+ //
+ // If we have output flow control based on
+ // the modem status lines, then we have to do
+ // all the modem work before we output each
+ // character. (Otherwise we might miss a
+ // status line change.)
+ //
+
+ if (Extension->HandFlow.ControlHandShake &
+ SERIAL_OUT_HANDSHAKEMASK) {
+
+ SerialHandleModemUpdate(
+ Extension,
+ TRUE
+ );
+
+ }
+
+ //
+ // We can only send the xon character if
+ // the only reason we are holding is because
+ // of the xoff. (Hardware flow control or
+ // sending break preclude putting a new character
+ // on the wire.)
+ //
+
+ if (Extension->SendXonChar &&
+ !(Extension->TXHolding & ~SERIAL_TX_XOFF)) {
+
+ if ((Extension->HandFlow.FlowReplace &
+ SERIAL_RTS_MASK) ==
+ SERIAL_TRANSMIT_TOGGLE) {
+
+ //
+ // We have to raise if we're sending
+ // this character.
+ //
+
+ SerialSetRTS(Extension->WdfInterrupt, Extension);
+
+ Extension->PerfStats.TransmittedCount++;
+ Extension->WmiPerfData.TransmittedCount++;
+
+ WRITE_TRANSMIT_HOLDING(Extension, Extension->Controller,
+ Extension->SpecialChars.XonChar);
+ SerialInsertQueueDpc(
+ Extension->StartTimerLowerRTSDpc
+ )?Extension->CountOfTryingToLowerRTS++:0;
+
+
+ } else {
+
+ Extension->PerfStats.TransmittedCount++;
+ Extension->WmiPerfData.TransmittedCount++;
+
+ WRITE_TRANSMIT_HOLDING(Extension,
+ Extension->Controller,
+ Extension->SpecialChars.XonChar);
+ }
+
+
+ Extension->SendXonChar = FALSE;
+ Extension->HoldingEmpty = FALSE;
+
+ //
+ // If we send an xon, by definition we
+ // can't be holding by Xoff.
+ //
+
+ Extension->TXHolding &= ~SERIAL_TX_XOFF;
+
+ //
+ // If we are sending an xon char then
+ // by definition we can't be "holding"
+ // up reception by Xoff.
+ //
+
+ Extension->RXHolding &= ~SERIAL_RX_XOFF;
+
+ } else if (Extension->SendXoffChar &&
+ !Extension->TXHolding) {
+
+ if ((Extension->HandFlow.FlowReplace &
+ SERIAL_RTS_MASK) ==
+ SERIAL_TRANSMIT_TOGGLE) {
+
+ //
+ // We have to raise if we're sending
+ // this character.
+ //
+
+ SerialSetRTS(Extension->WdfInterrupt, Extension);
+
+ Extension->PerfStats.TransmittedCount++;
+ Extension->WmiPerfData.TransmittedCount++;
+ WRITE_TRANSMIT_HOLDING(Extension,
+ Extension->Controller,
+ Extension->SpecialChars.XoffChar);
+
+ SerialInsertQueueDpc(
+ Extension->StartTimerLowerRTSDpc
+ )?Extension->CountOfTryingToLowerRTS++:0;
+
+ } else {
+
+ Extension->PerfStats.TransmittedCount++;
+ Extension->WmiPerfData.TransmittedCount++;
+ WRITE_TRANSMIT_HOLDING(Extension,
+ Extension->Controller,
+ Extension->SpecialChars.XoffChar);
+
+ }
+
+ //
+ // We can't be sending an Xoff character
+ // if the transmission is already held
+ // up because of Xoff. Therefore, if we
+ // are holding then we can't send the char.
+ //
+
+ //
+ // If the application has set xoff continue
+ // mode then we don't actually stop sending
+ // characters if we send an xoff to the other
+ // side.
+ //
+
+ if (!(Extension->HandFlow.FlowReplace &
+ SERIAL_XOFF_CONTINUE)) {
+
+ Extension->TXHolding |= SERIAL_TX_XOFF;
+
+ if ((Extension->HandFlow.FlowReplace &
+ SERIAL_RTS_MASK) ==
+ SERIAL_TRANSMIT_TOGGLE) {
+
+ SerialInsertQueueDpc(
+ Extension->StartTimerLowerRTSDpc
+ )?Extension->CountOfTryingToLowerRTS++:0;
+
+ }
+
+ }
+
+ Extension->SendXoffChar = FALSE;
+ Extension->HoldingEmpty = FALSE;
+
+ //
+ // Even if transmission is being held
+ // up, we should still transmit an immediate
+ // character if all that is holding us
+ // up is xon/xoff (OS/2 rules).
+ //
+
+ } else if (Extension->TransmitImmediate &&
+ (!Extension->TXHolding ||
+ (Extension->TXHolding == SERIAL_TX_XOFF)
+ )) {
+
+ Extension->TransmitImmediate = FALSE;
+
+ if ((Extension->HandFlow.FlowReplace &
+ SERIAL_RTS_MASK) ==
+ SERIAL_TRANSMIT_TOGGLE) {
+
+ //
+ // We have to raise if we're sending
+ // this character.
+ //
+
+ SerialSetRTS(Extension->WdfInterrupt, Extension);
+
+ Extension->PerfStats.TransmittedCount++;
+ Extension->WmiPerfData.TransmittedCount++;
+ WRITE_TRANSMIT_HOLDING(Extension,
+ Extension->Controller,
+ Extension->ImmediateChar);
+
+ SerialInsertQueueDpc(
+ Extension->StartTimerLowerRTSDpc
+ )?Extension->CountOfTryingToLowerRTS++:0;
+
+ } else {
+
+ Extension->PerfStats.TransmittedCount++;
+ Extension->WmiPerfData.TransmittedCount++;
+ WRITE_TRANSMIT_HOLDING(Extension,
+ Extension->Controller,
+ Extension->ImmediateChar);
+
+ }
+
+ Extension->HoldingEmpty = FALSE;
+
+ SerialInsertQueueDpc(
+ Extension->CompleteImmediateDpc
+ );
+
+ } else if (!Extension->TXHolding) {
+
+ ULONG amountToWrite;
+
+ if (Extension->FifoPresent) {
+
+ amountToWrite = (Extension->TxFifoAmount <
+ Extension->WriteLength)?
+ Extension->TxFifoAmount:
+ Extension->WriteLength;
+
+ } else {
+
+ amountToWrite = 1;
+
+ }
+ if ((Extension->HandFlow.FlowReplace &
+ SERIAL_RTS_MASK) ==
+ SERIAL_TRANSMIT_TOGGLE) {
+
+ //
+ // We have to raise if we're sending
+ // this character.
+ //
+
+ SerialSetRTS(Extension->WdfInterrupt, Extension);
+
+ if (amountToWrite == 1) {
+
+ Extension->PerfStats.TransmittedCount++;
+ Extension->WmiPerfData.TransmittedCount++;
+ WRITE_TRANSMIT_HOLDING(Extension,
+ Extension->Controller,
+ *(Extension->WriteCurrentChar));
+
+ } else {
+
+ Extension->PerfStats.TransmittedCount +=
+ amountToWrite;
+ Extension->WmiPerfData.TransmittedCount +=
+ amountToWrite;
+ WRITE_TRANSMIT_FIFO_HOLDING(Extension,
+ Extension->Controller,
+ Extension->WriteCurrentChar,
+ amountToWrite);
+ }
+
+ SerialInsertQueueDpc(
+ Extension->StartTimerLowerRTSDpc
+ )?Extension->CountOfTryingToLowerRTS++:0;
+
+ } else {
+
+ if (amountToWrite == 1) {
+
+ Extension->PerfStats.TransmittedCount++;
+ Extension->WmiPerfData.TransmittedCount++;
+ WRITE_TRANSMIT_HOLDING(Extension,
+ Extension->Controller,
+ *(Extension->WriteCurrentChar));
+
+ } else {
+
+ Extension->PerfStats.TransmittedCount +=
+ amountToWrite;
+ Extension->WmiPerfData.TransmittedCount +=
+ amountToWrite;
+ WRITE_TRANSMIT_FIFO_HOLDING(Extension,
+ Extension->Controller,
+ Extension->WriteCurrentChar,
+ amountToWrite);
+
+ }
+
+ }
+
+ Extension->HoldingEmpty = FALSE;
+ Extension->WriteCurrentChar += amountToWrite;
+ Extension->WriteLength -= amountToWrite;
+
+ if (!Extension->WriteLength) {
+
+ //
+ // No More characters left. This
+ // write is complete. Take care
+ // when updating the information field,
+ // we could have an xoff counter masquerading
+ // as a write request.
+ //
+ reqContext = SerialGetRequestContext(Extension->CurrentWriteRequest);
+
+ reqContext->Information =
+ (reqContext->MajorFunction == IRP_MJ_WRITE)?
+ (reqContext->Length): (1);
+
+ SerialInsertQueueDpc(
+ Extension->CompleteWriteDpc
+ );
+
+ }
+
+ }
+
+ }
+
+ break;
+
+ }
+
+ case SERIAL_IIR_MS: {
+
+ SerialHandleModemUpdate(
+ Extension,
+ FALSE
+ );
+
+ break;
+
+ }
+
+ }
+
+ } while (!((InterruptIdReg =
+ READ_INTERRUPT_ID_REG(Extension, Extension->Controller))
+ & SERIAL_IIR_NO_INTERRUPT_PENDING));
+
+ //
+ // Besides catching the WINBOND and SMC chip problems this
+ // will also cause transmission to restart incase of an xon
+ // char being received. Don't remove.
+ //
+
+ if (SerialProcessLSR(Extension) & SERIAL_LSR_THRE) {
+
+ if (!Extension->TXHolding &&
+ (Extension->WriteLength ||
+ Extension->TransmitImmediate)) {
+
+ goto doTrasmitStuff;
+
+ }
+
+ }
+
+ }
+
+ return ServicedAnInterrupt;
+
+}
+
+VOID
+SerialPutChar(
+ IN PSERIAL_DEVICE_EXTENSION Extension,
+ IN UCHAR CharToPut
+ )
+
+/*++
+
+Routine Description:
+
+ This routine, which only runs at device level, takes care of
+ placing a character into the typeahead (receive) buffer.
+
+Arguments:
+
+ Extension - The serial device extension.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PREQUEST_CONTEXT reqContext = NULL;
+
+ //
+ // If we have dsr sensitivity enabled then
+ // we need to check the modem status register
+ // to see if it has changed.
+ //
+
+ if (Extension->HandFlow.ControlHandShake &
+ SERIAL_DSR_SENSITIVITY) {
+
+ SerialHandleModemUpdate(
+ Extension,
+ FALSE
+ );
+
+ if (Extension->RXHolding & SERIAL_RX_DSR) {
+
+ //
+ // We simply act as if we haven't
+ // seen the character if we have dsr
+ // sensitivity and the dsr line is low.
+ //
+
+ return;
+
+ }
+
+ }
+
+ //
+ // If the xoff counter is non-zero then decrement it.
+ // If the counter then goes to zero, complete that request.
+ //
+
+ if (Extension->CountSinceXoff) {
+
+ Extension->CountSinceXoff--;
+
+ if (!Extension->CountSinceXoff) {
+ reqContext = SerialGetRequestContext(Extension->CurrentXoffRequest);
+ reqContext->Status = STATUS_SUCCESS;
+ reqContext->Information = 0;
+ SerialInsertQueueDpc(
+ Extension->XoffCountCompleteDpc
+ );
+
+ }
+
+ }
+
+ //
+ // Check to see if we are copying into the
+ // users buffer or into the interrupt buffer.
+ //
+ // If we are copying into the user buffer
+ // then we know there is always room for one more.
+ // (We know this because if there wasn't room
+ // then that read would have completed and we
+ // would be using the interrupt buffer.)
+ //
+ // If we are copying into the interrupt buffer
+ // then we will need to check if we have enough
+ // room.
+ //
+
+ if (Extension->ReadBufferBase !=
+ Extension->InterruptReadBuffer) {
+
+ //
+ // Increment the following value so
+ // that the interval timer (if one exists
+ // for this read) can know that a character
+ // has been read.
+ //
+
+ Extension->ReadByIsr++;
+
+ //
+ // We are in the user buffer. Place the
+ // character into the buffer. See if the
+ // read is complete.
+ //
+
+ *Extension->CurrentCharSlot = CharToPut;
+
+ if (Extension->CurrentCharSlot ==
+ Extension->LastCharSlot) {
+
+ //
+ // We've filled up the users buffer.
+ // Switch back to the interrupt buffer
+ // and send off a DPC to Complete the read.
+ //
+ // It is inherent that when we were using
+ // a user buffer that the interrupt buffer
+ // was empty.
+ //
+
+ Extension->ReadBufferBase =
+ Extension->InterruptReadBuffer;
+ Extension->CurrentCharSlot =
+ Extension->InterruptReadBuffer;
+ Extension->FirstReadableChar =
+ Extension->InterruptReadBuffer;
+ Extension->LastCharSlot =
+ Extension->InterruptReadBuffer +
+ (Extension->BufferSize - 1);
+ Extension->CharsInInterruptBuffer = 0;
+ reqContext = SerialGetRequestContext(Extension->CurrentReadRequest);
+ reqContext->Information = reqContext->Length;
+
+ SerialInsertQueueDpc(
+ Extension->CompleteReadDpc
+ );
+
+ } else {
+
+ //
+ // Not done with the users read.
+ //
+
+ Extension->CurrentCharSlot++;
+
+ }
+
+ } else {
+
+ //
+ // We need to see if we reached our flow
+ // control threshold. If we have then
+ // we turn on whatever flow control the
+ // owner has specified. If no flow
+ // control was specified, well..., we keep
+ // trying to receive characters and hope that
+ // we have enough room. Note that no matter
+ // what flow control protocol we are using, it
+ // will not prevent us from reading whatever
+ // characters are available.
+ //
+
+ if ((Extension->HandFlow.ControlHandShake
+ & SERIAL_DTR_MASK) ==
+ SERIAL_DTR_HANDSHAKE) {
+
+ //
+ // If we are already doing a
+ // dtr hold then we don't have
+ // to do anything else.
+ //
+
+ if (!(Extension->RXHolding &
+ SERIAL_RX_DTR)) {
+
+ if ((Extension->BufferSize -
+ Extension->HandFlow.XoffLimit)
+ <= (Extension->CharsInInterruptBuffer+1)) {
+
+ Extension->RXHolding |= SERIAL_RX_DTR;
+
+ SerialClrDTR(Extension->WdfInterrupt, Extension);
+
+ }
+
+ }
+
+ }
+
+ if ((Extension->HandFlow.FlowReplace
+ & SERIAL_RTS_MASK) ==
+ SERIAL_RTS_HANDSHAKE) {
+
+ //
+ // If we are already doing a
+ // rts hold then we don't have
+ // to do anything else.
+ //
+
+ if (!(Extension->RXHolding &
+ SERIAL_RX_RTS)) {
+
+ if ((Extension->BufferSize -
+ Extension->HandFlow.XoffLimit)
+ <= (Extension->CharsInInterruptBuffer+1)) {
+
+ Extension->RXHolding |= SERIAL_RX_RTS;
+
+ SerialClrRTS(Extension->WdfInterrupt, Extension);
+
+ }
+
+ }
+
+ }
+
+ if (Extension->HandFlow.FlowReplace &
+ SERIAL_AUTO_RECEIVE) {
+
+ //
+ // If we are already doing a
+ // xoff hold then we don't have
+ // to do anything else.
+ //
+
+ if (!(Extension->RXHolding &
+ SERIAL_RX_XOFF)) {
+
+ if ((Extension->BufferSize -
+ Extension->HandFlow.XoffLimit)
+ <= (Extension->CharsInInterruptBuffer+1)) {
+
+ Extension->RXHolding |= SERIAL_RX_XOFF;
+
+ //
+ // If necessary cause an
+ // off to be sent.
+ //
+
+ SerialProdXonXoff(
+ Extension,
+ FALSE
+ );
+
+ }
+
+ }
+
+ }
+
+ if (Extension->CharsInInterruptBuffer <
+ Extension->BufferSize) {
+
+ *Extension->CurrentCharSlot = CharToPut;
+ Extension->CharsInInterruptBuffer++;
+
+ //
+ // If we've become 80% full on this character
+ // and this is an interesting event, note it.
+ //
+
+ if (Extension->CharsInInterruptBuffer ==
+ Extension->BufferSizePt8) {
+
+ if (Extension->IsrWaitMask &
+ SERIAL_EV_RX80FULL) {
+
+ Extension->HistoryMask |= SERIAL_EV_RX80FULL;
+
+ if (Extension->IrpMaskLocation) {
+
+ *Extension->IrpMaskLocation =
+ Extension->HistoryMask;
+ Extension->IrpMaskLocation = NULL;
+ Extension->HistoryMask = 0;
+
+ reqContext = SerialGetRequestContext(Extension->CurrentWaitRequest);
+ reqContext->Information = sizeof(ULONG);
+ SerialInsertQueueDpc(
+ Extension->CommWaitDpc
+ );
+
+ }
+
+ }
+
+ }
+
+ //
+ // Point to the next available space
+ // for a received character. Make sure
+ // that we wrap around to the beginning
+ // of the buffer if this last character
+ // received was placed at the last slot
+ // in the buffer.
+ //
+
+ if (Extension->CurrentCharSlot ==
+ Extension->LastCharSlot) {
+
+ Extension->CurrentCharSlot =
+ Extension->InterruptReadBuffer;
+
+ } else {
+
+ Extension->CurrentCharSlot++;
+
+ }
+
+ } else {
+
+ //
+ // We have a new character but no room for it.
+ //
+
+ Extension->PerfStats.BufferOverrunErrorCount++;
+ Extension->WmiPerfData.BufferOverrunErrorCount++;
+ Extension->ErrorWord |= SERIAL_ERROR_QUEUEOVERRUN;
+
+ if (Extension->HandFlow.FlowReplace &
+ SERIAL_ERROR_CHAR) {
+
+ //
+ // Place the error character into the last
+ // valid place for a character. Be careful!,
+ // that place might not be the previous location!
+ //
+
+ if (Extension->CurrentCharSlot ==
+ Extension->InterruptReadBuffer) {
+
+ *(Extension->InterruptReadBuffer+
+ (Extension->BufferSize-1)) =
+ Extension->SpecialChars.ErrorChar;
+
+ } else {
+
+ *(Extension->CurrentCharSlot-1) =
+ Extension->SpecialChars.ErrorChar;
+
+ }
+
+ }
+
+ //
+ // If the application has requested it, abort all reads
+ // and writes on an error.
+ //
+
+ if (Extension->HandFlow.ControlHandShake &
+ SERIAL_ERROR_ABORT) {
+
+ SerialInsertQueueDpc(
+ Extension->CommErrorDpc
+ );
+
+ }
+
+ }
+
+ }
+
+}
+
+UCHAR
+SerialProcessLSR(
+ IN PSERIAL_DEVICE_EXTENSION Extension
+ )
+
+/*++
+
+Routine Description:
+
+ This routine, which only runs at device level, reads the
+ ISR and totally processes everything that might have
+ changed.
+
+Arguments:
+
+ Extension - The serial device extension.
+
+Return Value:
+
+ The value of the line status register.
+
+--*/
+
+{
+ PREQUEST_CONTEXT reqContext = NULL;
+
+ UCHAR LineStatus = READ_LINE_STATUS(Extension, Extension->Controller);
+
+
+ Extension->HoldingEmpty = (LineStatus & SERIAL_LSR_THRE) ? TRUE : FALSE;
+
+ //
+ // If the line status register is just the fact that
+ // the trasmit registers are empty or a character is
+ // received then we want to reread the interrupt
+ // identification register so that we just pick up that.
+ //
+
+ if (LineStatus & ~(SERIAL_LSR_THRE | SERIAL_LSR_TEMT
+ | SERIAL_LSR_DR)) {
+
+ //
+ // We have some sort of data problem in the receive.
+ // For any of these errors we may abort all current
+ // reads and writes.
+ //
+ //
+ // If we are inserting the value of the line status
+ // into the data stream then we should put the escape
+ // character in now.
+ //
+
+ if (Extension->EscapeChar) {
+
+ SerialPutChar(
+ Extension,
+ Extension->EscapeChar
+ );
+
+ SerialPutChar(
+ Extension,
+ (UCHAR)((LineStatus & SERIAL_LSR_DR)?
+ (SERIAL_LSRMST_LSR_DATA):(SERIAL_LSRMST_LSR_NODATA))
+ );
+
+ SerialPutChar(
+ Extension,
+ LineStatus
+ );
+
+ if (LineStatus & SERIAL_LSR_DR) {
+
+ Extension->PerfStats.ReceivedCount++;
+ Extension->WmiPerfData.ReceivedCount++;
+ SerialPutChar(
+ Extension,
+ READ_RECEIVE_BUFFER(Extension, Extension->Controller)
+ );
+
+ }
+
+ }
+
+ if (LineStatus & SERIAL_LSR_OE) {
+
+ Extension->PerfStats.SerialOverrunErrorCount++;
+ Extension->WmiPerfData.SerialOverrunErrorCount++;
+ Extension->ErrorWord |= SERIAL_ERROR_OVERRUN;
+
+ if (Extension->HandFlow.FlowReplace &
+ SERIAL_ERROR_CHAR) {
+
+ SerialPutChar(
+ Extension,
+ Extension->SpecialChars.ErrorChar
+ );
+
+ if (LineStatus & SERIAL_LSR_DR) {
+
+ Extension->PerfStats.ReceivedCount++;
+ Extension->WmiPerfData.ReceivedCount++;
+ READ_RECEIVE_BUFFER(Extension, Extension->Controller);
+
+ }
+
+ } else {
+
+ if (LineStatus & SERIAL_LSR_DR) {
+
+ Extension->PerfStats.ReceivedCount++;
+ Extension->WmiPerfData.ReceivedCount++;
+ SerialPutChar(
+ Extension,
+ READ_RECEIVE_BUFFER(Extension,
+ Extension->Controller
+ )
+ );
+
+ }
+
+ }
+
+ }
+
+ if (LineStatus & SERIAL_LSR_BI) {
+
+ Extension->ErrorWord |= SERIAL_ERROR_BREAK;
+
+ if (Extension->HandFlow.FlowReplace &
+ SERIAL_BREAK_CHAR) {
+
+ SerialPutChar(
+ Extension,
+ Extension->SpecialChars.BreakChar
+ );
+
+ }
+
+ } else {
+
+ //
+ // Framing errors only count if they
+ // occur exclusive of a break being
+ // received.
+ //
+
+ if (LineStatus & SERIAL_LSR_PE) {
+
+ Extension->PerfStats.ParityErrorCount++;
+ Extension->WmiPerfData.ParityErrorCount++;
+ Extension->ErrorWord |= SERIAL_ERROR_PARITY;
+
+ if (Extension->HandFlow.FlowReplace &
+ SERIAL_ERROR_CHAR) {
+
+ SerialPutChar(
+ Extension,
+ Extension->SpecialChars.ErrorChar
+ );
+
+ if (LineStatus & SERIAL_LSR_DR) {
+
+ Extension->PerfStats.ReceivedCount++;
+ Extension->WmiPerfData.ReceivedCount++;
+ READ_RECEIVE_BUFFER(Extension, Extension->Controller);
+
+ }
+
+ }
+
+ }
+
+ if (LineStatus & SERIAL_LSR_FE) {
+
+ Extension->PerfStats.FrameErrorCount++;
+ Extension->WmiPerfData.FrameErrorCount++;
+ Extension->ErrorWord |= SERIAL_ERROR_FRAMING;
+
+ if (Extension->HandFlow.FlowReplace &
+ SERIAL_ERROR_CHAR) {
+
+ SerialPutChar(
+ Extension,
+ Extension->SpecialChars.ErrorChar
+ );
+ if (LineStatus & SERIAL_LSR_DR) {
+
+ Extension->PerfStats.ReceivedCount++;
+ Extension->WmiPerfData.ReceivedCount++;
+ READ_RECEIVE_BUFFER(Extension, Extension->Controller);
+
+ }
+
+ }
+
+ }
+
+ }
+
+ //
+ // If the application has requested it,
+ // abort all the reads and writes
+ // on an error.
+ //
+
+ if (Extension->HandFlow.ControlHandShake &
+ SERIAL_ERROR_ABORT) {
+
+ SerialInsertQueueDpc(
+ Extension->CommErrorDpc
+ );
+
+ }
+
+ //
+ // Check to see if we have a wait
+ // pending on the comm error events. If we
+ // do then we schedule a dpc to satisfy
+ // that wait.
+ //
+
+ if (Extension->IsrWaitMask) {
+
+ if ((Extension->IsrWaitMask & SERIAL_EV_ERR) &&
+ (LineStatus & (SERIAL_LSR_OE |
+ SERIAL_LSR_PE |
+ SERIAL_LSR_FE))) {
+
+ Extension->HistoryMask |= SERIAL_EV_ERR;
+
+ }
+
+ if ((Extension->IsrWaitMask & SERIAL_EV_BREAK) &&
+ (LineStatus & SERIAL_LSR_BI)) {
+
+ Extension->HistoryMask |= SERIAL_EV_BREAK;
+
+ }
+
+ if (Extension->IrpMaskLocation &&
+ Extension->HistoryMask) {
+
+ *Extension->IrpMaskLocation =
+ Extension->HistoryMask;
+ Extension->IrpMaskLocation = NULL;
+ Extension->HistoryMask = 0;
+ reqContext = SerialGetRequestContext(Extension->CurrentWaitRequest);
+ reqContext->Information = sizeof(ULONG);
+ SerialInsertQueueDpc(
+ Extension->CommWaitDpc
+ );
+
+ }
+
+ }
+
+ if (LineStatus & SERIAL_LSR_THRE) {
+
+ //
+ // There is a hardware bug in some versions
+ // of the 16450 and 550. If THRE interrupt
+ // is pending, but a higher interrupt comes
+ // in it will only return the higher and
+ // *forget* about the THRE.
+ //
+ // A suitable workaround - whenever we
+ // are *all* done reading line status
+ // of the device we check to see if the
+ // transmit holding register is empty. If it is
+ // AND we are currently transmitting data
+ // enable the interrupts which should cause
+ // an interrupt indication which we quiet
+ // when we read the interrupt id register.
+ //
+
+ if (Extension->WriteLength |
+ Extension->TransmitImmediate) {
+
+ DISABLE_ALL_INTERRUPTS(Extension,
+ Extension->Controller
+ );
+ ENABLE_ALL_INTERRUPTS(Extension,
+ Extension->Controller
+ );
+ }
+
+ }
+
+ }
+
+ return LineStatus;
+}
+
+
diff --git a/serial/serial/log.c b/serial/serial/log.c
new file mode 100644
index 00000000..045badb3
--- /dev/null
+++ b/serial/serial/log.c
@@ -0,0 +1,97 @@
+/*++
+
+Copyright (c) Microsoft Corporation
+
+Module Name:
+
+ log.c
+
+Abstract:
+
+ Debug log Code for serial.
+
+Environment:
+
+ kernel mode only
+
+--*/
+
+#include "precomp.h"
+
+extern ULONG DebugLevel;
+extern ULONG DebugFlag;
+
+#if !defined(EVENT_TRACING)
+
+VOID
+SerialDbgPrintEx (
+ 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 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)) {
+
+ KdPrint((_DRIVER_NAME_": RtlStringCbVPrintfA failed %x\n", status));
+ return;
+ }
+ if (TraceEventsLevel < TRACE_LEVEL_INFORMATION ||
+ (TraceEventsLevel <= DebugLevel &&
+ ((TraceEventsFlag & DebugFlag) == TraceEventsFlag))) {
+
+ KdPrint((debugMessageBuffer));
+ }
+ }
+ va_end(list);
+
+ return;
+
+#else
+
+ UNREFERENCED_PARAMETER(TraceEventsLevel);
+ UNREFERENCED_PARAMETER(TraceEventsFlag);
+ UNREFERENCED_PARAMETER(DebugMessage);
+
+#endif
+}
+
+#endif
+
diff --git a/serial/serial/log.h b/serial/serial/log.h
new file mode 100644
index 00000000..ab41afd0
--- /dev/null
+++ b/serial/serial/log.h
@@ -0,0 +1,37 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+:ts=4
+
+Module Name:
+
+ log.h
+
+Abstract:
+
+ debug macros
+
+Environment:
+
+ Kernel & user mode
+
+--*/
+
+#ifndef __LOG_H__
+#define __LOG_H__
+
+#if !defined(EVENT_TRACING)
+
+VOID
+SerialDbgPrintEx (
+ IN ULONG DebugPrintLevel,
+ IN ULONG DebugPrintFlag,
+ IN PCCHAR DebugMessage,
+ ...
+ );
+
+#endif
+
+#endif // __LOG_H__
+
+
diff --git a/serial/serial/modmflow.c b/serial/serial/modmflow.c
new file mode 100644
index 00000000..97db8548
--- /dev/null
+++ b/serial/serial/modmflow.c
@@ -0,0 +1,1714 @@
+/*++
+
+Copyright (c) Microsoft Corporation
+
+Module Name:
+
+ modmflow.c
+
+Abstract:
+
+ This module contains *MOST* of the code used to manipulate
+ the modem control and status registers. The vast majority
+ of the remainder of flow control is concentrated in the
+ Interrupt service routine. A very small amount resides
+ in the read code that pull characters out of the interrupt
+ buffer.
+
+Environment:
+
+ Kernel mode
+
+--*/
+
+#include "precomp.h"
+
+#if defined(EVENT_TRACING)
+#include "modmflow.tmh"
+#endif
+
+
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialDecrementRTSCounter;
+
+BOOLEAN
+SerialSetDTR(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine which is only called at interrupt level is used
+ to set the DTR in the modem control register.
+
+Arguments:
+
+ Context - Really a pointer to the device extension.
+
+Return Value:
+
+ This routine always returns FALSE.
+
+--*/
+
+{
+ PSERIAL_DEVICE_EXTENSION Extension = Context;
+ UCHAR ModemControl;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ ModemControl = READ_MODEM_CONTROL(Extension, Extension->Controller);
+
+ ModemControl |= SERIAL_MCR_DTR;
+
+ SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_IOCTLS,
+ "Setting DTR for %p\n", Extension->Controller);
+
+ WRITE_MODEM_CONTROL(Extension, Extension->Controller, ModemControl);
+
+ return FALSE;
+
+}
+
+BOOLEAN
+SerialClrDTR(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine which is only called at interrupt level is used
+ to clear the DTR in the modem control register.
+
+Arguments:
+
+ Context - Really a pointer to the device extension.
+
+Return Value:
+
+ This routine always returns FALSE.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION Extension = Context;
+ UCHAR ModemControl;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ ModemControl = READ_MODEM_CONTROL(Extension, Extension->Controller);
+
+ ModemControl &= ~SERIAL_MCR_DTR;
+
+ SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Clearing DTR for %p\n", Extension->Controller);
+
+ WRITE_MODEM_CONTROL(Extension, Extension->Controller, ModemControl);
+
+ return FALSE;
+
+}
+
+BOOLEAN
+SerialSetRTS(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine which is only called at interrupt level is used
+ to set the RTS in the modem control register.
+
+Arguments:
+
+ Context - Really a pointer to the device extension.
+
+Return Value:
+
+ This routine always returns FALSE.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION Extension = Context;
+ UCHAR ModemControl;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ ModemControl = READ_MODEM_CONTROL(Extension, Extension->Controller);
+
+ ModemControl |= SERIAL_MCR_RTS;
+
+ SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Setting Rts for %p\n", Extension->Controller);
+
+ WRITE_MODEM_CONTROL(Extension, Extension->Controller, ModemControl);
+
+ return FALSE;
+
+}
+
+BOOLEAN
+SerialClrRTS(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine which is only called at interrupt level is used
+ to clear the RTS in the modem control register.
+
+Arguments:
+
+ Context - Really a pointer to the device extension.
+
+Return Value:
+
+ This routine always returns FALSE.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION Extension = Context;
+ UCHAR ModemControl;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ ModemControl = READ_MODEM_CONTROL(Extension, Extension->Controller);
+
+ ModemControl &= ~SERIAL_MCR_RTS;
+
+ SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Clearing Rts for %p\n", Extension->Controller);
+
+ WRITE_MODEM_CONTROL(Extension, Extension->Controller, ModemControl);
+
+ return FALSE;
+
+}
+
+BOOLEAN
+SerialSetupNewHandFlow(
+ IN PSERIAL_DEVICE_EXTENSION Extension,
+ IN PSERIAL_HANDFLOW NewHandFlow
+ )
+
+/*++
+
+Routine Description:
+
+ This routine adjusts the flow control based on new
+ control flow.
+
+Arguments:
+
+ Extension - A pointer to the serial device extension.
+
+ NewHandFlow - A pointer to a serial handflow structure
+ that is to become the new setup for flow
+ control.
+
+Return Value:
+
+ This routine always returns FALSE.
+
+--*/
+
+{
+
+ SERIAL_HANDFLOW New = *NewHandFlow;
+
+ //
+ // If the Extension->DeviceIsOpened is FALSE that means
+ // we are entering this routine in response to an open request.
+ // If that is so, then we always proceed with the work regardless
+ // of whether things have changed.
+ //
+
+ //
+ // First we take care of the DTR flow control. We only
+ // do work if something has changed.
+ //
+
+ if ((!Extension->DeviceIsOpened) ||
+ ((Extension->HandFlow.ControlHandShake & SERIAL_DTR_MASK) !=
+ (New.ControlHandShake & SERIAL_DTR_MASK))) {
+
+ SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Processing DTR flow for %p\n",
+ Extension->Controller);
+
+ if (New.ControlHandShake & SERIAL_DTR_MASK) {
+
+ //
+ // Well we might want to set DTR.
+ //
+ // Before we do, we need to check whether we are doing
+ // dtr flow control. If we are then we need to check
+ // if then number of characters in the interrupt buffer
+ // exceeds the XoffLimit. If it does then we don't
+ // enable DTR AND we set the RXHolding to record that
+ // we are holding because of the dtr.
+ //
+
+ if ((New.ControlHandShake & SERIAL_DTR_MASK)
+ == SERIAL_DTR_HANDSHAKE) {
+
+ if ((Extension->BufferSize - New.XoffLimit) >
+ Extension->CharsInInterruptBuffer) {
+
+ //
+ // However if we are already holding we don't want
+ // to turn it back on unless we exceed the Xon
+ // limit.
+ //
+
+ if (Extension->RXHolding & SERIAL_RX_DTR) {
+
+ //
+ // We can assume that its DTR line is already low.
+ //
+
+ if (Extension->CharsInInterruptBuffer >
+ (ULONG)New.XonLimit) {
+
+ SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Removing DTR block on "
+ "reception for %p\n",
+ Extension->Controller);
+
+ Extension->RXHolding &= ~SERIAL_RX_DTR;
+ SerialSetDTR(Extension->WdfInterrupt, Extension);
+
+ }
+
+ } else {
+
+ SerialSetDTR(Extension->WdfInterrupt, Extension);
+
+ }
+
+ } else {
+
+ SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Setting DTR block on reception "
+ "for %p\n", Extension->Controller);
+ Extension->RXHolding |= SERIAL_RX_DTR;
+ SerialClrDTR(Extension->WdfInterrupt, Extension);
+
+ }
+
+ } else {
+
+ //
+ // Note that if we aren't currently doing dtr flow control then
+ // we MIGHT have been. So even if we aren't currently doing
+ // DTR flow control, we should still check if RX is holding
+ // because of DTR. If it is, then we should clear the holding
+ // of this bit.
+ //
+
+ if (Extension->RXHolding & SERIAL_RX_DTR) {
+ SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Removing dtr block of reception "
+ "for %p\n", Extension->Controller);
+ Extension->RXHolding &= ~SERIAL_RX_DTR;
+ }
+
+ SerialSetDTR(Extension->WdfInterrupt, Extension);
+
+ }
+
+ } else {
+
+ //
+ // The end result here will be that DTR is cleared.
+ //
+ // We first need to check whether reception is being held
+ // up because of previous DTR flow control. If it is then
+ // we should clear that reason in the RXHolding mask.
+ //
+
+ if (Extension->RXHolding & SERIAL_RX_DTR) {
+
+ SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "removing dtr block of reception for"
+ " %p\n", Extension->Controller);
+ Extension->RXHolding &= ~SERIAL_RX_DTR;
+
+ }
+
+ SerialClrDTR(Extension->WdfInterrupt, Extension);
+
+ }
+
+ }
+
+ //
+ // Time to take care of the RTS Flow control.
+ //
+ // First we only do work if something has changed.
+ //
+
+ if ((!Extension->DeviceIsOpened) ||
+ ((Extension->HandFlow.FlowReplace & SERIAL_RTS_MASK) !=
+ (New.FlowReplace & SERIAL_RTS_MASK))) {
+
+ SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Processing RTS flow %p\n",
+ Extension->Controller);
+
+ if ((New.FlowReplace & SERIAL_RTS_MASK) ==
+ SERIAL_RTS_HANDSHAKE) {
+
+ //
+ // Well we might want to set RTS.
+ //
+ // Before we do, we need to check whether we are doing
+ // rts flow control. If we are then we need to check
+ // if then number of characters in the interrupt buffer
+ // exceeds the XoffLimit. If it does then we don't
+ // enable RTS AND we set the RXHolding to record that
+ // we are holding because of the rts.
+ //
+
+ if ((Extension->BufferSize - New.XoffLimit) >
+ Extension->CharsInInterruptBuffer) {
+
+ //
+ // However if we are already holding we don't want
+ // to turn it back on unless we exceed the Xon
+ // limit.
+ //
+
+ if (Extension->RXHolding & SERIAL_RX_RTS) {
+
+ //
+ // We can assume that its RTS line is already low.
+ //
+
+ if (Extension->CharsInInterruptBuffer >
+ (ULONG)New.XonLimit) {
+
+ SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Removing rts block of "
+ "reception for %p\n",
+ Extension->Controller);
+ Extension->RXHolding &= ~SERIAL_RX_RTS;
+ SerialSetRTS(Extension->WdfInterrupt, Extension);
+
+ }
+
+ } else {
+
+ SerialSetRTS(Extension->WdfInterrupt, Extension);
+
+ }
+
+ } else {
+
+ SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Setting rts block of reception for "
+ "%p\n", Extension->Controller);
+ Extension->RXHolding |= SERIAL_RX_RTS;
+ SerialClrRTS(Extension->WdfInterrupt, Extension);
+
+ }
+
+ } else if ((New.FlowReplace & SERIAL_RTS_MASK) ==
+ SERIAL_RTS_CONTROL) {
+
+ //
+ // Note that if we aren't currently doing rts flow control then
+ // we MIGHT have been. So even if we aren't currently doing
+ // RTS flow control, we should still check if RX is holding
+ // because of RTS. If it is, then we should clear the holding
+ // of this bit.
+ //
+
+ if (Extension->RXHolding & SERIAL_RX_RTS) {
+
+ SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Clearing rts block of reception for "
+ "%p\n", Extension->Controller);
+ Extension->RXHolding &= ~SERIAL_RX_RTS;
+
+ }
+
+ SerialSetRTS(Extension->WdfInterrupt, Extension);
+
+ } else if ((New.FlowReplace & SERIAL_RTS_MASK) ==
+ SERIAL_TRANSMIT_TOGGLE) {
+
+ //
+ // We first need to check whether reception is being held
+ // up because of previous RTS flow control. If it is then
+ // we should clear that reason in the RXHolding mask.
+ //
+
+ if (Extension->RXHolding & SERIAL_RX_RTS) {
+
+ SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "TOGGLE Clearing rts block of "
+ "reception for %p\n", Extension->Controller);
+ Extension->RXHolding &= ~SERIAL_RX_RTS;
+
+ }
+
+ //
+ // We have to place the rts value into the Extension
+ // now so that the code that tests whether the
+ // rts line should be lowered will find that we
+ // are "still" doing transmit toggling. The code
+ // for lowering can be invoked later by a timer so
+ // it has to test whether it still needs to do its
+ // work.
+ //
+
+ Extension->HandFlow.FlowReplace &= ~SERIAL_RTS_MASK;
+ Extension->HandFlow.FlowReplace |= SERIAL_TRANSMIT_TOGGLE;
+
+ //
+ // The order of the tests is very important below.
+ //
+ // If there is a break then we should turn on the RTS.
+ //
+ // If there isn't a break but there are characters in
+ // the hardware, then turn on the RTS.
+ //
+ // If there are writes pending that aren't being held
+ // up, then turn on the RTS.
+ //
+
+ if ((Extension->TXHolding & SERIAL_TX_BREAK) ||
+ ((SerialProcessLSR(Extension) & (SERIAL_LSR_THRE |
+ SERIAL_LSR_TEMT)) !=
+ (SERIAL_LSR_THRE |
+ SERIAL_LSR_TEMT)) ||
+ (Extension->CurrentWriteRequest || Extension->TransmitImmediate ||
+ (!IsQueueEmpty(Extension->WriteQueue)) &&
+ (!Extension->TXHolding))) {
+
+ SerialSetRTS(Extension->WdfInterrupt, Extension);
+
+ } else {
+
+ //
+ // This routine will check to see if it is time
+ // to lower the RTS because of transmit toggle
+ // being on. If it is ok to lower it, it will,
+ // if it isn't ok, it will schedule things so
+ // that it will get lowered later.
+ //
+
+ Extension->CountOfTryingToLowerRTS++;
+ SerialPerhapsLowerRTS(Extension->WdfInterrupt, Extension);
+
+ }
+
+ } else {
+
+ //
+ // The end result here will be that RTS is cleared.
+ //
+ // We first need to check whether reception is being held
+ // up because of previous RTS flow control. If it is then
+ // we should clear that reason in the RXHolding mask.
+ //
+
+ if (Extension->RXHolding & SERIAL_RX_RTS) {
+
+ SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Clearing rts block of reception for"
+ " %p\n", Extension->Controller);
+ Extension->RXHolding &= ~SERIAL_RX_RTS;
+
+ }
+
+ SerialClrRTS(Extension->WdfInterrupt, Extension);
+
+ }
+
+ }
+
+ //
+ // We now take care of automatic receive flow control.
+ // We only do work if things have changed.
+ //
+
+ if ((!Extension->DeviceIsOpened) ||
+ ((Extension->HandFlow.FlowReplace & SERIAL_AUTO_RECEIVE) !=
+ (New.FlowReplace & SERIAL_AUTO_RECEIVE))) {
+
+ if (New.FlowReplace & SERIAL_AUTO_RECEIVE) {
+
+ //
+ // We wouldn't be here if it had been on before.
+ //
+ // We should check to see whether we exceed the turn
+ // off limits.
+ //
+ // Note that since we are following the OS/2 flow
+ // control rules we will never send an xon if
+ // when enabling xon/xoff flow control we discover that
+ // we could receive characters but we are held up do
+ // to a previous Xoff.
+ //
+
+ if ((Extension->BufferSize - New.XoffLimit) <=
+ Extension->CharsInInterruptBuffer) {
+
+ //
+ // Cause the Xoff to be sent.
+ //
+
+ Extension->RXHolding |= SERIAL_RX_XOFF;
+
+ SerialProdXonXoff(
+ Extension,
+ FALSE
+ );
+
+ }
+
+ } else {
+
+ //
+ // The app has disabled automatic receive flow control.
+ //
+ // If transmission was being held up because of
+ // an automatic receive Xoff, then we should
+ // cause an Xon to be sent.
+ //
+
+ if (Extension->RXHolding & SERIAL_RX_XOFF) {
+
+ Extension->RXHolding &= ~SERIAL_RX_XOFF;
+
+ //
+ // Cause the Xon to be sent.
+ //
+
+ SerialProdXonXoff(
+ Extension,
+ TRUE
+ );
+
+ }
+
+ }
+
+ }
+
+ //
+ // We now take care of automatic transmit flow control.
+ // We only do work if things have changed.
+ //
+
+ if ((!Extension->DeviceIsOpened) ||
+ ((Extension->HandFlow.FlowReplace & SERIAL_AUTO_TRANSMIT) !=
+ (New.FlowReplace & SERIAL_AUTO_TRANSMIT))) {
+
+ if (New.FlowReplace & SERIAL_AUTO_TRANSMIT) {
+
+ //
+ // We wouldn't be here if it had been on before.
+ //
+ // There is some belief that if autotransmit
+ // was just enabled, I should go look in what we
+ // already received, and if we find the xoff character
+ // then we should stop transmitting. I think this
+ // is an application bug. For now we just care about
+ // what we see in the future.
+ //
+
+ ;
+
+ } else {
+
+ //
+ // The app has disabled automatic transmit flow control.
+ //
+ // If transmission was being held up because of
+ // an automatic transmit Xoff, then we should
+ // cause an Xon to be sent.
+ //
+
+ if (Extension->TXHolding & SERIAL_TX_XOFF) {
+
+ Extension->TXHolding &= ~SERIAL_TX_XOFF;
+
+ //
+ // Cause the Xon to be sent.
+ //
+
+ SerialProdXonXoff(
+ Extension,
+ TRUE
+ );
+
+ }
+
+ }
+
+ }
+
+ //
+ // At this point we can simply make sure that entire
+ // handflow structure in the extension is updated.
+ //
+
+ Extension->HandFlow = New;
+
+ return FALSE;
+
+}
+
+BOOLEAN
+SerialSetHandFlow(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to set the handshake and control
+ flow in the device extension.
+
+Arguments:
+
+ Context - Pointer to a structure that contains a pointer to
+ the device extension and a pointer to a handflow
+ structure..
+
+Return Value:
+
+ This routine always returns FALSE.
+
+--*/
+
+{
+
+ PSERIAL_IOCTL_SYNC S = Context;
+ PSERIAL_DEVICE_EXTENSION Extension = S->Extension;
+ PSERIAL_HANDFLOW HandFlow = S->Data;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ SerialSetupNewHandFlow(
+ Extension,
+ HandFlow
+ );
+
+ SerialHandleModemUpdate(
+ Extension,
+ FALSE
+ );
+
+ return FALSE;
+
+}
+
+BOOLEAN
+SerialTurnOnBreak(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine will turn on break in the hardware and
+ record the fact the break is on, in the extension variable
+ that holds reasons that transmission is stopped.
+
+Arguments:
+
+ Context - Really a pointer to the device extension.
+
+Return Value:
+
+ This routine always returns FALSE.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION Extension = Context;
+
+ UCHAR OldLineControl;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ if ((Extension->HandFlow.FlowReplace & SERIAL_RTS_MASK) ==
+ SERIAL_TRANSMIT_TOGGLE) {
+
+ SerialSetRTS(Extension->WdfInterrupt, Extension);
+
+ }
+
+ OldLineControl = READ_LINE_CONTROL(Extension, Extension->Controller);
+
+ OldLineControl |= SERIAL_LCR_BREAK;
+
+ WRITE_LINE_CONTROL(Extension,
+ Extension->Controller,
+ OldLineControl
+ );
+
+ Extension->TXHolding |= SERIAL_TX_BREAK;
+
+ return FALSE;
+
+}
+
+BOOLEAN
+SerialTurnOffBreak(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine will turn off break in the hardware and
+ record the fact the break is off, in the extension variable
+ that holds reasons that transmission is stopped.
+
+Arguments:
+
+ Context - Really a pointer to the device extension.
+
+Return Value:
+
+ This routine always returns FALSE.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION Extension = Context;
+
+ UCHAR OldLineControl;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ if (Extension->TXHolding & SERIAL_TX_BREAK) {
+
+ //
+ // We actually have a good reason for testing if transmission
+ // is holding instead of blindly clearing the bit.
+ //
+ // If transmission actually was holding and the result of
+ // clearing the bit is that we should restart transmission
+ // then we will poke the interrupt enable bit, which will
+ // cause an actual interrupt and transmission will then
+ // restart on its own.
+ //
+ // If transmission wasn't holding and we poked the bit
+ // then we would interrupt before a character actually made
+ // it out and we could end up over writing a character in
+ // the transmission hardware.
+
+ OldLineControl = READ_LINE_CONTROL(Extension, Extension->Controller);
+
+ OldLineControl &= ~SERIAL_LCR_BREAK;
+
+ WRITE_LINE_CONTROL(Extension,
+ Extension->Controller,
+ OldLineControl
+ );
+
+ Extension->TXHolding &= ~SERIAL_TX_BREAK;
+
+ if (!Extension->TXHolding &&
+ (Extension->TransmitImmediate ||
+ Extension->WriteLength) &&
+ Extension->HoldingEmpty) {
+
+ DISABLE_ALL_INTERRUPTS(Extension, Extension->Controller);
+ ENABLE_ALL_INTERRUPTS(Extension, Extension->Controller);
+
+ } else {
+
+ //
+ // The following routine will lower the rts if we
+ // are doing transmit toggleing and there is no
+ // reason to keep it up.
+ //
+
+ Extension->CountOfTryingToLowerRTS++;
+ SerialPerhapsLowerRTS(Extension->WdfInterrupt, Extension);
+
+ }
+
+ }
+
+ return FALSE;
+
+}
+
+BOOLEAN
+SerialPretendXoff(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to process the Ioctl that request the
+ driver to act as if an Xoff was received. Even if the
+ driver does not have automatic Xoff/Xon flowcontrol - This
+ still will stop the transmission. This is the OS/2 behavior
+ and is not well specified for Windows. Therefore we adopt
+ the OS/2 behavior.
+
+ Note: If the driver does not have automatic Xoff/Xon enabled
+ then the only way to restart transmission is for the
+ application to request we "act" as if we saw the xon.
+
+Arguments:
+
+ Context - Really a pointer to the device extension.
+
+Return Value:
+
+ This routine always returns FALSE.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION Extension = Context;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ Extension->TXHolding |= SERIAL_TX_XOFF;
+
+ if ((Extension->HandFlow.FlowReplace & SERIAL_RTS_MASK) ==
+ SERIAL_TRANSMIT_TOGGLE) {
+
+ SerialInsertQueueDpc(
+ Extension->StartTimerLowerRTSDpc
+ )?Extension->CountOfTryingToLowerRTS++:0;
+
+ }
+
+ return FALSE;
+
+}
+
+BOOLEAN
+SerialPretendXon(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to process the Ioctl that request the
+ driver to act as if an Xon was received.
+
+ Note: If the driver does not have automatic Xoff/Xon enabled
+ then the only way to restart transmission is for the
+ application to request we "act" as if we saw the xon.
+
+Arguments:
+
+ Context - Really a pointer to the device extension.
+
+Return Value:
+
+ This routine always returns FALSE.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION Extension = Context;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ if (Extension->TXHolding) {
+
+ //
+ // We actually have a good reason for testing if transmission
+ // is holding instead of blindly clearing the bit.
+ //
+ // If transmission actually was holding and the result of
+ // clearing the bit is that we should restart transmission
+ // then we will poke the interrupt enable bit, which will
+ // cause an actual interrupt and transmission will then
+ // restart on its own.
+ //
+ // If transmission wasn't holding and we poked the bit
+ // then we would interrupt before a character actually made
+ // it out and we could end up over writing a character in
+ // the transmission hardware.
+
+ Extension->TXHolding &= ~SERIAL_TX_XOFF;
+
+ if (!Extension->TXHolding &&
+ (Extension->TransmitImmediate ||
+ Extension->WriteLength) &&
+ Extension->HoldingEmpty) {
+
+ DISABLE_ALL_INTERRUPTS(Extension, Extension->Controller);
+ ENABLE_ALL_INTERRUPTS(Extension, Extension->Controller);
+
+ }
+
+ }
+
+ return FALSE;
+
+}
+
+VOID
+SerialHandleReducedIntBuffer(
+ IN PSERIAL_DEVICE_EXTENSION Extension
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called to handle a reduction in the number
+ of characters in the interrupt (typeahead) buffer. It
+ will check the current output flow control and re-enable transmission
+ as needed.
+
+ NOTE: This routine assumes that it is working at interrupt level.
+
+Arguments:
+
+ Extension - A pointer to the device extension.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+
+ //
+ // If we are doing receive side flow control and we are
+ // currently "holding" then because we've emptied out
+ // some characters from the interrupt buffer we need to
+ // see if we can "re-enable" reception.
+ //
+
+ if (Extension->RXHolding) {
+
+ if (Extension->CharsInInterruptBuffer <=
+ (ULONG)Extension->HandFlow.XonLimit) {
+
+ if (Extension->RXHolding & SERIAL_RX_DTR) {
+
+ Extension->RXHolding &= ~SERIAL_RX_DTR;
+ SerialSetDTR(Extension->WdfInterrupt, Extension);
+
+ }
+
+ if (Extension->RXHolding & SERIAL_RX_RTS) {
+
+ Extension->RXHolding &= ~SERIAL_RX_RTS;
+ SerialSetRTS(Extension->WdfInterrupt, Extension);
+
+ }
+
+ if (Extension->RXHolding & SERIAL_RX_XOFF) {
+
+ //
+ // Prod the transmit code to send xon.
+ //
+
+ SerialProdXonXoff(
+ Extension,
+ TRUE
+ );
+
+ }
+
+ }
+
+ }
+
+}
+
+VOID
+SerialProdXonXoff(
+ IN PSERIAL_DEVICE_EXTENSION Extension,
+ IN BOOLEAN SendXon
+ )
+
+/*++
+
+Routine Description:
+
+ This routine will set up the SendXxxxChar variables if
+ necessary and determine if we are going to be interrupting
+ because of current transmission state. It will cause an
+ interrupt to occur if neccessary, to send the xon/xoff char.
+
+ NOTE: This routine assumes that it is called at interrupt
+ level.
+
+Arguments:
+
+ Extension - A pointer to the serial device extension.
+
+ SendXon - If a character is to be send, this indicates whether
+ it should be an Xon or an Xoff.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ //
+ // We assume that if the prodding is called more than
+ // once that the last prod has set things up appropriately.
+ //
+ // We could get called before the character is sent out
+ // because the send of the character was blocked because
+ // of hardware flow control (or break).
+ //
+
+ if (!Extension->SendXonChar && !Extension->SendXoffChar
+ && Extension->HoldingEmpty) {
+
+ DISABLE_ALL_INTERRUPTS(Extension, Extension->Controller);
+ ENABLE_ALL_INTERRUPTS(Extension, Extension->Controller);
+
+ }
+
+ if (SendXon) {
+
+ Extension->SendXonChar = TRUE;
+ Extension->SendXoffChar = FALSE;
+
+ } else {
+
+ Extension->SendXonChar = FALSE;
+ Extension->SendXoffChar = TRUE;
+
+ }
+
+}
+
+ULONG
+SerialHandleModemUpdate(
+ IN PSERIAL_DEVICE_EXTENSION Extension,
+ IN BOOLEAN DoingTX
+ )
+
+/*++
+
+Routine Description:
+
+ This routine will be to check on the modem status, and
+ handle any appropriate event notification as well as
+ any flow control appropriate to modem status lines.
+
+ NOTE: This routine assumes that it is called at interrupt
+ level.
+
+Arguments:
+
+ Extension - A pointer to the serial device extension.
+
+ DoingTX - This boolean is used to indicate that this call
+ came from the transmit processing code. If this
+ is true then there is no need to cause a new interrupt
+ since the code will be trying to send the next
+ character as soon as this call finishes.
+
+Return Value:
+
+ This returns the old value of the modem status register
+ (extended into a ULONG).
+
+--*/
+
+{
+
+ //
+ // We keep this local so that after we are done
+ // examining the modem status and we've updated
+ // the transmission holding value, we know whether
+ // we've changed from needing to hold up transmission
+ // to transmission being able to proceed.
+ //
+ ULONG OldTXHolding = Extension->TXHolding;
+
+ //
+ // Holds the value in the mode status register.
+ //
+ UCHAR ModemStatus;
+ PREQUEST_CONTEXT reqContext;
+
+ ModemStatus =
+ READ_MODEM_STATUS(Extension, Extension->Controller);
+
+
+ //
+ // If we are placeing the modem status into the data stream
+ // on every change, we should do it now.
+ //
+
+ if (Extension->EscapeChar) {
+
+ if (ModemStatus & (SERIAL_MSR_DCTS |
+ SERIAL_MSR_DDSR |
+ SERIAL_MSR_TERI |
+ SERIAL_MSR_DDCD)) {
+
+ SerialPutChar(
+ Extension,
+ Extension->EscapeChar
+ );
+ SerialPutChar(
+ Extension,
+ SERIAL_LSRMST_MST
+ );
+ SerialPutChar(
+ Extension,
+ ModemStatus
+ );
+
+ }
+
+ }
+
+
+ //
+ // Take care of input flow control based on sensitivity
+ // to the DSR. This is done so that the application won't
+ // see spurious data generated by odd devices.
+ //
+ // Basically, if we are doing dsr sensitivity then the
+ // driver should only accept data when the dsr bit is
+ // set.
+ //
+
+ if (Extension->HandFlow.ControlHandShake & SERIAL_DSR_SENSITIVITY) {
+
+ if (ModemStatus & SERIAL_MSR_DSR) {
+
+ //
+ // The line is high. Simply make sure that
+ // RXHolding does't have the DSR bit.
+ //
+
+ Extension->RXHolding &= ~SERIAL_RX_DSR;
+
+ } else {
+
+ Extension->RXHolding |= SERIAL_RX_DSR;
+
+ }
+
+ } else {
+
+ //
+ // We don't have sensitivity due to DSR. Make sure we
+ // arn't holding. (We might have been, but the app just
+ // asked that we don't hold for this reason any more.)
+ //
+
+ Extension->RXHolding &= ~SERIAL_RX_DSR;
+
+ }
+
+ //
+ // Check to see if we have a wait
+ // pending on the modem status events. If we
+ // do then we schedule a dpc to satisfy
+ // that wait.
+ //
+
+ if (Extension->IsrWaitMask) {
+
+ if ((Extension->IsrWaitMask & SERIAL_EV_CTS) &&
+ (ModemStatus & SERIAL_MSR_DCTS)) {
+
+ Extension->HistoryMask |= SERIAL_EV_CTS;
+
+ }
+
+ if ((Extension->IsrWaitMask & SERIAL_EV_DSR) &&
+ (ModemStatus & SERIAL_MSR_DDSR)) {
+
+ Extension->HistoryMask |= SERIAL_EV_DSR;
+
+ }
+
+ if ((Extension->IsrWaitMask & SERIAL_EV_RING) &&
+ (ModemStatus & SERIAL_MSR_TERI)) {
+
+ Extension->HistoryMask |= SERIAL_EV_RING;
+
+ }
+
+ if ((Extension->IsrWaitMask & SERIAL_EV_RLSD) &&
+ (ModemStatus & SERIAL_MSR_DDCD)) {
+
+ Extension->HistoryMask |= SERIAL_EV_RLSD;
+
+ }
+
+ if (Extension->IrpMaskLocation &&
+ Extension->HistoryMask) {
+
+ *Extension->IrpMaskLocation =
+ Extension->HistoryMask;
+ Extension->IrpMaskLocation = NULL;
+ Extension->HistoryMask = 0;
+
+ reqContext = SerialGetRequestContext(Extension->CurrentWaitRequest);
+ reqContext->Information = sizeof(ULONG);
+ SerialInsertQueueDpc(
+ Extension->CommWaitDpc
+ );
+
+ }
+
+ }
+
+ //
+ // If the app has modem line flow control then
+ // we check to see if we have to hold up transmission.
+ //
+
+ if (Extension->HandFlow.ControlHandShake &
+ SERIAL_OUT_HANDSHAKEMASK) {
+
+ if (Extension->HandFlow.ControlHandShake &
+ SERIAL_CTS_HANDSHAKE) {
+
+ if (ModemStatus & SERIAL_MSR_CTS) {
+
+ Extension->TXHolding &= ~SERIAL_TX_CTS;
+
+ } else {
+
+ Extension->TXHolding |= SERIAL_TX_CTS;
+
+ }
+
+ } else {
+
+ Extension->TXHolding &= ~SERIAL_TX_CTS;
+
+ }
+
+ if (Extension->HandFlow.ControlHandShake &
+ SERIAL_DSR_HANDSHAKE) {
+
+ if (ModemStatus & SERIAL_MSR_DSR) {
+
+ Extension->TXHolding &= ~SERIAL_TX_DSR;
+
+ } else {
+
+ Extension->TXHolding |= SERIAL_TX_DSR;
+
+ }
+
+ } else {
+
+ Extension->TXHolding &= ~SERIAL_TX_DSR;
+
+ }
+
+ if (Extension->HandFlow.ControlHandShake &
+ SERIAL_DCD_HANDSHAKE) {
+
+ if (ModemStatus & SERIAL_MSR_DCD) {
+
+ Extension->TXHolding &= ~SERIAL_TX_DCD;
+
+ } else {
+
+ Extension->TXHolding |= SERIAL_TX_DCD;
+
+ }
+
+ } else {
+
+ Extension->TXHolding &= ~SERIAL_TX_DCD;
+
+ }
+
+ //
+ // If we hadn't been holding, and now we are then
+ // queue off a dpc that will lower the RTS line
+ // if we are doing transmit toggling.
+ //
+
+ if (!OldTXHolding && Extension->TXHolding &&
+ ((Extension->HandFlow.FlowReplace & SERIAL_RTS_MASK) ==
+ SERIAL_TRANSMIT_TOGGLE)) {
+
+ SerialInsertQueueDpc(
+ Extension->StartTimerLowerRTSDpc
+ )?Extension->CountOfTryingToLowerRTS++:0;
+
+ }
+
+ //
+ // We've done any adjusting that needed to be
+ // done to the holding mask given updates
+ // to the modem status. If the Holding mask
+ // is clear (and it wasn't clear to start)
+ // and we have "write" work to do set things
+ // up so that the transmission code gets invoked.
+ //
+
+ if (!DoingTX && OldTXHolding && !Extension->TXHolding) {
+
+ if (!Extension->TXHolding &&
+ (Extension->TransmitImmediate ||
+ Extension->WriteLength) &&
+ Extension->HoldingEmpty) {
+
+ DISABLE_ALL_INTERRUPTS(Extension, Extension->Controller);
+ ENABLE_ALL_INTERRUPTS(Extension, Extension->Controller);
+ }
+
+ }
+
+ } else {
+
+ //
+ // We need to check if transmission is holding
+ // up because of modem status lines. What
+ // could have occured is that for some strange
+ // reason, the app has asked that we no longer
+ // stop doing output flow control based on
+ // the modem status lines. If however, we
+ // *had* been held up because of the status lines
+ // then we need to clear up those reasons.
+ //
+
+ if (Extension->TXHolding & (SERIAL_TX_DCD |
+ SERIAL_TX_DSR |
+ SERIAL_TX_CTS)) {
+
+ Extension->TXHolding &= ~(SERIAL_TX_DCD |
+ SERIAL_TX_DSR |
+ SERIAL_TX_CTS);
+
+
+ if (!DoingTX && OldTXHolding && !Extension->TXHolding) {
+
+ if (!Extension->TXHolding &&
+ (Extension->TransmitImmediate ||
+ Extension->WriteLength) &&
+ Extension->HoldingEmpty) {
+
+ DISABLE_ALL_INTERRUPTS(Extension, Extension->Controller);
+ ENABLE_ALL_INTERRUPTS(Extension, Extension->Controller);
+ }
+
+ }
+
+ }
+
+ }
+
+ return ((ULONG)ModemStatus);
+}
+
+BOOLEAN
+SerialPerhapsLowerRTS(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine checks that the software reasons for lowering
+ the RTS lines are present. If so, it will then cause the
+ line status register to be read (and any needed processing
+ implied by the status register to be done), and if the
+ shift register is empty it will lower the line. If the
+ shift register isn't empty, this routine will queue off
+ a dpc that will start a timer, that will basically call
+ us back to try again.
+
+ NOTE: This routine assumes that it is called at interrupt
+ level.
+
+Arguments:
+
+ Context - Really a pointer to the device extension.
+
+Return Value:
+
+ Always FALSE.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION Extension = Context;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ //
+ // We first need to test if we are actually still doing
+ // transmit toggle flow control. If we aren't then
+ // we have no reason to try be here.
+ //
+
+ if ((Extension->HandFlow.FlowReplace & SERIAL_RTS_MASK) ==
+ SERIAL_TRANSMIT_TOGGLE) {
+
+ //
+ // The order of the tests is very important below.
+ //
+ // If there is a break then we should leave on the RTS,
+ // because when the break is turned off, it will submit
+ // the code to shut down the RTS.
+ //
+ // If there are writes pending that aren't being held
+ // up, then leave on the RTS, because the end of the write
+ // code will cause this code to be reinvoked. If the writes
+ // are being held up, its ok to lower the RTS because the
+ // upon trying to write the first character after transmission
+ // is restarted, we will raise the RTS line.
+ //
+
+ if ((Extension->TXHolding & SERIAL_TX_BREAK) ||
+ (Extension->CurrentWriteRequest || Extension->TransmitImmediate ||
+ (!IsQueueEmpty(Extension->WriteQueue)) &&
+ (!Extension->TXHolding))) {
+
+ NOTHING;
+
+ } else {
+
+ //
+ // Looks good so far. Call the line status check and processing
+ // code, it will return the "current" line status value. If
+ // the holding and shift register are clear, lower the RTS line,
+ // if they aren't clear, queue of a dpc that will cause a timer
+ // to reinvoke us later. We do this code here because no one
+ // but this routine cares about the characters in the hardware,
+ // so no routine by this routine will bother invoking to test
+ // if the hardware is empty.
+ //
+
+ if ((SerialProcessLSR(Extension) &
+ (SERIAL_LSR_THRE | SERIAL_LSR_TEMT)) !=
+ (SERIAL_LSR_THRE | SERIAL_LSR_TEMT)) {
+
+ //
+ // Well it's not empty, try again later.
+ //
+
+ SerialInsertQueueDpc(
+ Extension->StartTimerLowerRTSDpc
+ )?Extension->CountOfTryingToLowerRTS++:0;
+
+
+ } else {
+
+ //
+ // Nothing in the hardware, Lower the RTS.
+ //
+
+ SerialClrRTS(Extension->WdfInterrupt, Extension);
+
+
+ }
+
+ }
+
+ }
+
+ //
+ // We decement the counter to indicate that we've reached
+ // the end of the execution path that is trying to push
+ // down the RTS line.
+ //
+
+ Extension->CountOfTryingToLowerRTS--;
+
+ return FALSE;
+}
+
+VOID
+SerialStartTimerLowerRTS(
+ IN WDFDPC Dpc
+ )
+
+/*++
+
+Routine Description:
+
+ This routine starts a timer that when it expires will start
+ a dpc that will check if it can lower the rts line because
+ there are no characters in the hardware.
+
+Arguments:
+
+ Dpc - Not Used.
+
+ DeferredContext - Really points to the device extension.
+
+ SystemContext1 - Not Used.
+
+ SystemContext2 - Not Used.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LARGE_INTEGER CharTime;
+ PSERIAL_DEVICE_EXTENSION Extension = NULL;
+
+ Extension = SerialGetDeviceExtension(WdfDpcGetParentObject(Dpc));
+
+
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS, ">SerialStartTimerLowerRTS(%p)\n",
+ Extension);
+
+
+ //
+ // Since all the callbacks into the driver are serialized, we don't have
+ // synchronize the access to any of the Extension variables.
+ //
+
+ CharTime = SerialGetCharTime(Extension);
+
+ CharTime.QuadPart = -CharTime.QuadPart;
+
+ if (SerialSetTimer(
+ Extension->LowerRTSTimer,
+ CharTime
+ )) {
+
+ //
+ // The timer was already in the timer queue. This implies
+ // that one path of execution that was trying to lower
+ // the RTS has "died". Synchronize with the ISR so that
+ // we can lower the count.
+ //
+
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SerialDecrementRTSCounter,
+ Extension
+ );
+
+ }
+
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS, "<SerialStartTimerLowerRTS\n");
+
+}
+
+VOID
+SerialInvokePerhapsLowerRTS(
+ IN WDFTIMER Timer
+ )
+
+/*++
+
+Routine Description:
+
+ This dpc routine exists solely to call the code that
+ tests if the rts line should be lowered when TRANSMIT
+ TOGGLE flow control is being used.
+
+Arguments:
+
+ WDFTIMER
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION Extension = NULL;
+
+ Extension = SerialGetDeviceExtension(WdfTimerGetParentObject(Timer));
+
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SerialPerhapsLowerRTS,
+ Extension
+ );
+
+}
+
+BOOLEAN
+SerialDecrementRTSCounter(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine checks that the software reasons for lowering
+ the RTS lines are present. If so, it will then cause the
+ line status register to be read (and any needed processing
+ implied by the status register to be done), and if the
+ shift register is empty it will lower the line. If the
+ shift register isn't empty, this routine will queue off
+ a dpc that will start a timer, that will basically call
+ us back to try again.
+
+ NOTE: This routine assumes that it is called at interrupt
+ level.
+
+Arguments:
+
+ Context - Really a pointer to the device extension.
+
+Return Value:
+
+ Always FALSE.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION Extension = Context;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ Extension->CountOfTryingToLowerRTS--;
+
+ return FALSE;
+
+}
+
+
diff --git a/serial/serial/openclos.c b/serial/serial/openclos.c
new file mode 100644
index 00000000..a6310440
--- /dev/null
+++ b/serial/serial/openclos.c
@@ -0,0 +1,850 @@
+/*++
+
+Copyright (c) Microsoft Corporation
+
+Module Name:
+
+ openclos.c
+
+Abstract:
+
+ This module contains the code that is very specific to
+ opening, closing, and cleaning up in the serial driver.
+
+Environment:
+
+ Kernel mode
+
+--*/
+
+#include "precomp.h"
+
+#if defined(EVENT_TRACING)
+#include "openclos.tmh"
+#endif
+
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGESER,SerialGetCharTime)
+#pragma alloc_text(PAGESER,SerialEvtFileClose)
+#pragma alloc_text(PAGESER,SerialDrainUART)
+#pragma alloc_text(PAGESRP0,SerialEvtDeviceFileCreate)
+#pragma alloc_text(PAGESRP0,SerialCreateTimersAndDpcs)
+#endif // ALLOC_PRAGMA
+
+
+
+VOID
+SerialEvtDeviceFileCreate (
+ 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:
+
+ VOID.
+
+--*/
+{
+ NTSTATUS status;
+ PSERIAL_DEVICE_EXTENSION extension = SerialGetDeviceExtension (Device);
+
+ UNREFERENCED_PARAMETER(FileObject);
+
+ PAGED_CODE();
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_CREATE_CLOSE,
+ "SerialEvtDeviceFileCreate %wZ\n", &extension->DeviceName);
+
+ status = SerialDeviceFileCreateWorker(Device);
+
+ //
+ // Complete the WDF request.
+ //
+ WdfRequestComplete(Request, status);
+
+ return;
+
+}
+
+
+NTSTATUS
+SerialWdmDeviceFileCreate (
+ IN WDFDEVICE Device,
+ IN PIRP Irp
+ )
+/*++
+
+Routine Description:
+
+ This is the dispatch routine for IRP_MJ_CREATE. 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.
+
+Arguments:
+
+ DeviceObject - Pointer to the device object for this device
+ Irp - Pointer to the IRP for the current request
+
+Return Value:
+
+ NT status code
+
+--*/
+{
+ NTSTATUS status;
+ PSERIAL_DEVICE_EXTENSION extension = SerialGetDeviceExtension (Device);
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_CREATE_CLOSE,
+ "SerialWdmDeviceFileCreate %wZ\n", &extension->DeviceName);
+
+ status = SerialDeviceFileCreateWorker(Device);
+
+ //
+ // Complete the WDM request.
+ //
+ Irp->IoStatus.Information = 0L;
+ Irp->IoStatus.Status = status;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ return status;
+}
+
+
+NTSTATUS
+SerialDeviceFileCreateWorker (
+ IN WDFDEVICE Device
+ )
+{
+ NTSTATUS status;
+ PSERIAL_DEVICE_EXTENSION extension = SerialGetDeviceExtension (Device);
+
+ //
+ // Create a buffer for the RX data when no reads are outstanding.
+ //
+
+ extension->InterruptReadBuffer = NULL;
+ extension->BufferSize = 0;
+
+ switch (MmQuerySystemSize()) {
+
+ case MmLargeSystem: {
+
+ extension->BufferSize = 4096;
+ extension->InterruptReadBuffer = ExAllocatePoolWithTag(
+ NonPagedPool,
+ extension->BufferSize,
+ POOL_TAG
+ );
+
+ if (extension->InterruptReadBuffer) {
+ break;
+ }
+
+ }
+
+ case MmMediumSystem: {
+
+ extension->BufferSize = 1024;
+ extension->InterruptReadBuffer = ExAllocatePoolWithTag(
+ NonPagedPool,
+ extension->BufferSize,
+ POOL_TAG
+ );
+
+ if (extension->InterruptReadBuffer) {
+ break;
+ }
+
+ }
+
+ case MmSmallSystem: {
+
+ extension->BufferSize = 128;
+ extension->InterruptReadBuffer = ExAllocatePoolWithTag(
+ NonPagedPool,
+ extension->BufferSize,
+ POOL_TAG
+ );
+
+ }
+
+ }
+
+ if (!extension->InterruptReadBuffer) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ //
+ // By taking a power reference by calling WdfDeviceStopIdle, we prevent the
+ // framework from powering down our device due to idle timeout when there
+ // is an open handle. Power reference also moves the device to D0 if we are
+ // idled out. If you fail create anywhere later in this routine, do make sure
+ // drop the reference.
+ //
+ status = WdfDeviceStopIdle(Device, TRUE);
+ if (!NT_SUCCESS(status)) {
+ return status;
+ }
+
+ //
+ // wakeup is not currently enabled
+ //
+
+ extension->IsWakeEnabled = FALSE;
+
+ //
+ // On a new open we "flush" the read queue by initializing the
+ // count of characters.
+ //
+
+ extension->CharsInInterruptBuffer = 0;
+ extension->LastCharSlot = extension->InterruptReadBuffer +
+ (extension->BufferSize - 1);
+
+ extension->ReadBufferBase = extension->InterruptReadBuffer;
+ extension->CurrentCharSlot = extension->InterruptReadBuffer;
+ extension->FirstReadableChar = extension->InterruptReadBuffer;
+
+ extension->TotalCharsQueued = 0;
+
+ //
+ // We set up the default xon/xoff limits.
+ //
+
+ extension->HandFlow.XoffLimit = extension->BufferSize >> 3;
+ extension->HandFlow.XonLimit = extension->BufferSize >> 1;
+
+ extension->WmiCommData.XoffXmitThreshold = extension->HandFlow.XoffLimit;
+ extension->WmiCommData.XonXmitThreshold = extension->HandFlow.XonLimit;
+
+ extension->BufferSizePt8 = ((3*(extension->BufferSize>>2))+
+ (extension->BufferSize>>4));
+
+ //
+ // Mark the device as busy for WMI
+ //
+
+ extension->WmiCommData.IsBusy = TRUE;
+
+ extension->IrpMaskLocation = NULL;
+ extension->HistoryMask = 0;
+ extension->IsrWaitMask = 0;
+
+ extension->SendXonChar = FALSE;
+ extension->SendXoffChar = FALSE;
+
+#if !DBG
+ //
+ // Clear out the statistics.
+ //
+
+ WdfInterruptSynchronize(
+ extension->WdfInterrupt,
+ SerialClearStats,
+ extension
+ );
+#endif
+
+ //
+ // The escape char replacement must be reset upon every open.
+ //
+
+ extension->EscapeChar = 0;
+
+ //
+ // We don't want the device to be removed or stopped when there is an handle
+ //
+ // Note to anyone copying this sample as a starting point:
+ //
+ // This works in this driver simply because this driver supports exactly
+ // one open handle at a time. If it supported more, then it would need
+ // counting logic to determine when all the reasons for failing Stop/Remove
+ // were gone.
+ //
+ WdfDeviceSetStaticStopRemove(Device, FALSE);
+
+ //
+ // Synchronize with the ISR and let it know that the device
+ // has been successfully opened.
+ //
+
+ WdfInterruptSynchronize(
+ extension->WdfInterrupt,
+ SerialMarkOpen,
+ extension
+ );
+
+ return STATUS_SUCCESS;
+
+}
+
+
+VOID
+SerialEvtFileClose(
+ IN WDFFILEOBJECT FileObject
+ )
+
+/*++
+
+ 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
+
+--*/
+
+{
+ PAGED_CODE();
+
+ SerialFileCloseWorker(WdfFileObjectGetDevice(FileObject));
+ return;
+}
+
+
+NTSTATUS
+SerialWdmFileClose (
+ IN WDFDEVICE Device,
+ IN PIRP Irp
+ )
+/*++
+
+Routine Description:
+
+ This is the dispatch routine for IRP_MJ_CLOSE. This is called when all the
+ handles represented by the FileObject is closed and all the references to
+ the FileObject is removed.
+
+Arguments:
+
+ DeviceObject - Pointer to the device object for this device
+ Irp - Pointer to the IRP for the current request
+
+Return Value:
+
+ NT status code
+
+--*/
+{
+ SerialFileCloseWorker(Device);
+
+ Irp->IoStatus.Information = 0L;
+ Irp->IoStatus.Status = STATUS_SUCCESS;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ return STATUS_SUCCESS;
+}
+
+
+VOID
+SerialFileCloseWorker(
+ IN WDFDEVICE Device
+ )
+{
+ ULONG flushCount;
+
+ //
+ // This "timer value" is used to wait 10 character times
+ // after the hardware is empty before we actually "run down"
+ // all of the flow control/break junk.
+ //
+ LARGE_INTEGER tenCharDelay;
+
+ //
+ // Holds a character time.
+ //
+ LARGE_INTEGER charTime;
+
+ PSERIAL_DEVICE_EXTENSION extension = SerialGetDeviceExtension(Device);
+ PSERIAL_INTERRUPT_CONTEXT interruptContext = SerialGetInterruptContext(extension->WdfInterrupt);
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_CREATE_CLOSE, "In SerialEvtFileClose %wZ\n",
+ &extension->DeviceName);
+
+ //
+ // Acquire the interrupt state lock.
+ //
+ WdfWaitLockAcquire(interruptContext->InterruptStateLock, NULL);
+
+ //
+ // If the Interrupts are connected, then the hardware state has to be
+ // cleaned up now. Note that the EvtFileClose callback gets called for
+ // an open file object even though the interrupts have been disabled
+ // possibly due to a Surprise Remove PNP event. In such a case, the
+ // Interrupt object should not be used.
+ //
+ if (interruptContext->IsInterruptConnected) {
+
+ charTime.QuadPart = -SerialGetCharTime(extension).QuadPart;
+
+ //
+ // Do this now so that if the isr gets called it won't do anything
+ // to cause more chars to get sent. We want to run down the hardware.
+ //
+
+ SetDeviceIsOpened(extension, FALSE, FALSE);
+
+ //
+ // Synchronize with the isr to turn off break if it
+ // is already on.
+ //
+
+ WdfInterruptSynchronize(
+ extension->WdfInterrupt,
+ SerialTurnOffBreak,
+ extension
+ );
+
+ //
+ // Wait a reasonable amount of time (20 * fifodepth) until all characters
+ // have been emptied out of the hardware.
+ //
+
+ for (flushCount = (20 * 16); flushCount != 0; flushCount--) {
+ if ((READ_LINE_STATUS(extension, extension->Controller) &
+ (SERIAL_LSR_THRE | SERIAL_LSR_TEMT)) !=
+ (SERIAL_LSR_THRE | SERIAL_LSR_TEMT)) {
+
+ KeDelayExecutionThread(KernelMode, FALSE, &charTime);
+ } else {
+ break;
+ }
+ }
+
+ if (flushCount == 0) {
+ SerialMarkHardwareBroken(extension);
+ }
+
+ //
+ // Synchronize with the ISR to let it know that interrupts are
+ // no longer important.
+ //
+
+ WdfInterruptSynchronize(
+ extension->WdfInterrupt,
+ SerialMarkClose,
+ extension
+ );
+
+
+ //
+ // If the driver has automatically transmitted an Xoff in
+ // the context of automatic receive flow control then we
+ // should transmit an Xon.
+ //
+
+ if (extension->RXHolding & SERIAL_RX_XOFF) {
+
+ //
+ // Loop until the holding register is empty.
+ //
+ while (!(READ_LINE_STATUS(extension, extension->Controller) &
+ SERIAL_LSR_THRE)) {
+ KeDelayExecutionThread(
+ KernelMode,
+ FALSE,
+ &charTime
+ );
+
+ }
+
+ WRITE_TRANSMIT_HOLDING(extension,
+ extension->Controller,
+ extension->SpecialChars.XonChar
+ );
+
+ //
+ // Wait a reasonable amount of time for the characters
+ // to be emptied out of the hardware.
+ //
+
+ for (flushCount = (20 * 16); flushCount != 0; flushCount--) {
+ if ((READ_LINE_STATUS(extension, extension->Controller) &
+ (SERIAL_LSR_THRE | SERIAL_LSR_TEMT)) !=
+ (SERIAL_LSR_THRE | SERIAL_LSR_TEMT)) {
+ KeDelayExecutionThread(KernelMode, FALSE, &charTime);
+ } else {
+ break;
+ }
+ }
+
+ if (flushCount == 0) {
+ SerialMarkHardwareBroken(extension);
+ }
+ }
+
+
+ //
+ // The hardware is empty. Delay 10 character times before
+ // shut down all the flow control.
+ //
+
+ tenCharDelay.QuadPart = charTime.QuadPart * 10;
+
+ KeDelayExecutionThread(
+ KernelMode,
+ TRUE,
+ &tenCharDelay
+ );
+
+#pragma prefast(suppress: __WARNING_INFERRED_IRQ_TOO_LOW, "This warning is because we are calling interrupt synchronize routine directly.")
+ SerialClrDTR(extension->WdfInterrupt, extension);
+
+ //
+ // We have to be very careful how we clear the RTS line.
+ // Transmit toggling might have been on at some point.
+ //
+ // We know that there is nothing left that could start
+ // out the "polling" execution path. We need to
+ // check the counter that indicates that the execution
+ // path is active. If it is then we loop delaying one
+ // character time. After each delay we check to see if
+ // the counter has gone to zero. When it has we know that
+ // the execution path should be just about finished. We
+ // make sure that we still aren't in the routine that
+ // synchronized execution with the ISR by synchronizing
+ // ourselve with the ISR.
+ //
+
+ if (extension->CountOfTryingToLowerRTS) {
+
+ do {
+#pragma prefast(suppress: __WARNING_INFERRED_IRQ_TOO_HIGH, "This warning is due to suppressing the previous one.")
+ KeDelayExecutionThread(
+ KernelMode,
+ FALSE,
+ &charTime
+ );
+
+ } while (extension->CountOfTryingToLowerRTS);
+
+ //
+ // The execution path should no longer exist that
+ // is trying to push down the RTS. Well just
+ // make sure it's down by falling through to
+ // code that forces it down.
+ //
+
+ }
+
+#pragma prefast(suppress: __WARNING_INFERRED_IRQ_TOO_LOW, "This warning is because we are calling interrupt synchronize routine directly.")
+ SerialClrRTS(extension->WdfInterrupt, extension);
+
+ //
+ // Clean out the holding reasons (since we are closed).
+ //
+
+ extension->RXHolding = 0;
+ extension->TXHolding = 0;
+
+ //
+ // Mark device as not busy for WMI
+ //
+
+ extension->WmiCommData.IsBusy = FALSE;
+
+ }
+
+ //
+ // Release the Interrupt state lock.
+ //
+ WdfWaitLockRelease(interruptContext->InterruptStateLock);
+
+ //
+ // All is done. The port has been disabled from interrupting
+ // so there is no point in keeping the memory around.
+ //
+
+ extension->BufferSize = 0;
+ if (extension->InterruptReadBuffer != NULL) {
+ ExFreePool(extension->InterruptReadBuffer);
+ }
+ extension->InterruptReadBuffer = NULL;
+
+ //
+ // Make sure the wake is disabled.
+ //
+ ASSERT(!extension->IsWakeEnabled);
+
+ SerialDrainTimersAndDpcs(extension);
+
+ SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_CREATE_CLOSE, "DPC's drained:\n");
+
+ //
+ // It's fine for the device to be powered off if there are no open handles.
+ //
+ WdfDeviceResumeIdle(Device);
+
+ //
+ // It's okay to allow the device to be stopped or removed.
+ //
+ // Note to anyone copying this sample as a starting point:
+ //
+ // This works in this driver simply because this driver supports exactly
+ // one open handle at a time. If it supported more, then it would need
+ // counting logic to determine when all the reasons for failing Stop/Remove
+ // were gone.
+ //
+ WdfDeviceSetStaticStopRemove(Device, TRUE);
+
+ return;
+
+}
+
+BOOLEAN
+SerialMarkOpen(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine merely sets a boolean to true to mark the fact that
+ somebody opened the device and its worthwhile to pay attention
+ to interrupts.
+
+Arguments:
+
+ Context - Really a pointer to the device extension.
+
+Return Value:
+
+ This routine always returns FALSE.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION extension = Context;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ SerialReset(extension->WdfInterrupt, extension);
+
+ //
+ // Prepare for the opening by re-enabling interrupts.
+ //
+ // We do this my modifying the OUT2 line in the modem control.
+ // In PC's this bit is "anded" with the interrupt line.
+ //
+
+ WRITE_MODEM_CONTROL(extension,
+ extension->Controller,
+ (UCHAR)(READ_MODEM_CONTROL(extension, extension->Controller) | SERIAL_MCR_OUT2)
+ );
+
+ extension->DeviceIsOpened = TRUE;
+ extension->ErrorWord = 0;
+
+ return FALSE;
+
+}
+
+VOID
+SerialDrainUART(IN PSERIAL_DEVICE_EXTENSION PDevExt,
+ IN PLARGE_INTEGER PDrainTime)
+{
+ PAGED_CODE();
+
+ //
+ // Wait until all characters have been emptied out of the hardware.
+ //
+
+ while ((READ_LINE_STATUS(PDevExt, PDevExt->Controller) &
+ (SERIAL_LSR_THRE | SERIAL_LSR_TEMT))
+ != (SERIAL_LSR_THRE | SERIAL_LSR_TEMT)) {
+ KeDelayExecutionThread(KernelMode, FALSE, PDrainTime);
+ }
+}
+
+VOID
+SerialDisableUART(IN PVOID Context)
+
+/*++
+
+Routine Description:
+
+ This routine disables the UART and puts it in a "safe" state when
+ not in use (like a close or powerdown).
+
+Arguments:
+
+ Context - Really a pointer to the device extension.
+
+Return Value:
+
+ This routine always returns FALSE.
+
+--*/
+
+{
+ PSERIAL_DEVICE_EXTENSION extension = Context;
+
+ //
+ // Prepare for the closing by stopping interrupts.
+ //
+ // We do this by adjusting the OUT2 line in the modem control.
+ // In PC's this bit is "anded" with the interrupt line.
+ //
+
+ WRITE_MODEM_CONTROL(extension, extension->Controller,
+ (UCHAR)(READ_MODEM_CONTROL(extension, extension->Controller)
+ & ~SERIAL_MCR_OUT2));
+
+ if (extension->FifoPresent) {
+ WRITE_FIFO_CONTROL(extension, extension->Controller, (UCHAR)0);
+ }
+}
+
+
+
+BOOLEAN
+SerialMarkClose(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine merely sets a boolean to false to mark the fact that
+ somebody closed the device and it's no longer worthwhile to pay attention
+ to interrupts. It also disables the UART.
+
+Arguments:
+
+ Context - Really a pointer to the device extension.
+
+Return Value:
+
+ This routine always returns FALSE.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION extension = Context;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ SerialDisableUART(Context);
+ extension->DeviceIsOpened = FALSE;
+ extension->DeviceState.Reopen = FALSE;
+
+ return FALSE;
+
+}
+
+LARGE_INTEGER
+SerialGetCharTime(
+ IN PSERIAL_DEVICE_EXTENSION Extension
+ )
+
+/*++
+
+Routine Description:
+
+ This function will return the number of 100 nanosecond intervals
+ there are in one character time (based on the present form
+ of flow control.
+
+Arguments:
+
+ Extension - Just what it says.
+
+Return Value:
+
+ 100 nanosecond intervals in a character time.
+
+--*/
+
+{
+ ULONG dataSize = 0;
+ ULONG paritySize;
+ ULONG stopSize;
+ ULONG charTime;
+ ULONG bitTime;
+ LARGE_INTEGER tmp;
+
+ PAGED_CODE();
+
+ if ((Extension->LineControl & SERIAL_DATA_MASK) == SERIAL_5_DATA) {
+ dataSize = 5;
+ } else if ((Extension->LineControl & SERIAL_DATA_MASK)
+ == SERIAL_6_DATA) {
+ dataSize = 6;
+ } else if ((Extension->LineControl & SERIAL_DATA_MASK)
+ == SERIAL_7_DATA) {
+ dataSize = 7;
+ } else if ((Extension->LineControl & SERIAL_DATA_MASK)
+ == SERIAL_8_DATA) {
+ dataSize = 8;
+ }
+
+ paritySize = 1;
+ if ((Extension->LineControl & SERIAL_PARITY_MASK)
+ == SERIAL_NONE_PARITY) {
+
+ paritySize = 0;
+
+ }
+
+ if (Extension->LineControl & SERIAL_2_STOP) {
+
+ //
+ // Even if it is 1.5, for sanities sake were going
+ // to say 2.
+ //
+
+ stopSize = 2;
+
+ } else {
+
+ stopSize = 1;
+
+ }
+
+ //
+ // First we calculate the number of 100 nanosecond intervals
+ // are in a single bit time (Approximately).
+ //
+
+ bitTime = (10000000+(Extension->CurrentBaud-1))/Extension->CurrentBaud;
+ charTime = bitTime + ((dataSize+paritySize+stopSize)*bitTime);
+
+ tmp.QuadPart = charTime;
+ return tmp;
+
+}
+
+
diff --git a/serial/serial/pnp.c b/serial/serial/pnp.c
new file mode 100644
index 00000000..1feb5958
--- /dev/null
+++ b/serial/serial/pnp.c
@@ -0,0 +1,2800 @@
+/*++
+
+Copyright (c) 1991, 1992, 1993 - 1997 Microsoft Corporation
+
+Module Name:
+
+ pnp.c
+
+Abstract:
+
+ This module contains the code that handles the plug and play
+ IRPs for the serial driver.
+
+Environment:
+
+ Kernel mode
+
+--*/
+
+#include "precomp.h"
+#include <initguid.h>
+#include <ntddser.h>
+#include <stdlib.h>
+
+#if defined(EVENT_TRACING)
+#include "pnp.tmh"
+#endif
+
+static const PHYSICAL_ADDRESS SerialPhysicalZero = {0};
+static const SUPPORTED_BAUD_RATES SupportedBaudRates[] = {
+ {75, SERIAL_BAUD_075},
+ {110, SERIAL_BAUD_110},
+ {135, SERIAL_BAUD_134_5},
+ {150, SERIAL_BAUD_150},
+ {300, SERIAL_BAUD_300},
+ {600, SERIAL_BAUD_600},
+ {1200, SERIAL_BAUD_1200},
+ {1800, SERIAL_BAUD_1800},
+ {2400, SERIAL_BAUD_2400},
+ {4800, SERIAL_BAUD_4800},
+ {7200, SERIAL_BAUD_7200},
+ {9600, SERIAL_BAUD_9600},
+ {14400, SERIAL_BAUD_14400},
+ {19200, SERIAL_BAUD_19200},
+ {38400, SERIAL_BAUD_38400},
+ {56000, SERIAL_BAUD_56K},
+ {57600, SERIAL_BAUD_57600},
+ {115200, SERIAL_BAUD_115200},
+ {128000, SERIAL_BAUD_128K},
+ {SERIAL_BAUD_INVALID, SERIAL_BAUD_USER}
+ };
+
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGESRP0, SerialEvtDeviceAdd)
+#pragma alloc_text(PAGESRP0, SerialEvtPrepareHardware)
+#pragma alloc_text(PAGESRP0, SerialEvtReleaseHardware)
+#pragma alloc_text(PAGESRP0, SerialEvtDeviceD0ExitPreInterruptsDisabled)
+#pragma alloc_text(PAGESRP0, SerialMapHWResources)
+#pragma alloc_text(PAGESRP0, SerialUnmapHWResources)
+#pragma alloc_text(PAGESRP0, SerialEvtDeviceContextCleanup)
+#pragma alloc_text(PAGESRP0, SerialDoExternalNaming)
+#pragma alloc_text(PAGESRP0, SerialReportMaxBaudRate)
+#pragma alloc_text(PAGESRP0, SerialUndoExternalNaming)
+#pragma alloc_text(PAGESRP0, SerialInitController)
+#pragma alloc_text(PAGESRP0, SerialGetMappedAddress)
+#pragma alloc_text(PAGESRP0, SerialSetPowerPolicy)
+#pragma alloc_text(PAGESRP0, SerialReadSymName)
+
+#endif // ALLOC_PRAGMA
+
+PVOID LocalMmMapIoSpace(
+ _In_ PHYSICAL_ADDRESS PhysicalAddress,
+ _In_ SIZE_T NumberOfBytes
+ )
+{
+ typedef
+ PVOID
+ (*PFN_MM_MAP_IO_SPACE_EX) (
+ _In_ PHYSICAL_ADDRESS PhysicalAddress,
+ _In_ SIZE_T NumberOfBytes,
+ _In_ ULONG Protect
+ );
+
+ UNICODE_STRING name;
+ PFN_MM_MAP_IO_SPACE_EX pMmMapIoSpaceEx;
+
+ RtlInitUnicodeString(&name, L"MmMapIoSpaceEx");
+ pMmMapIoSpaceEx = (PFN_MM_MAP_IO_SPACE_EX) (ULONG_PTR)MmGetSystemRoutineAddress(&name);
+
+ if (pMmMapIoSpaceEx != NULL){
+ //
+ // Call WIN10 API if available
+ //
+ return pMmMapIoSpaceEx(PhysicalAddress,
+ NumberOfBytes,
+ PAGE_READWRITE | PAGE_NOCACHE);
+ }
+
+ return MmMapIoSpace(PhysicalAddress, NumberOfBytes, MmNonCached);
+}
+
+NTSTATUS
+SerialEvtDeviceAdd(
+ IN WDFDRIVER Driver,
+ IN PWDFDEVICE_INIT DeviceInit
+ )
+/*++
+
+Routine Description:
+
+ EvtDeviceAdd is called by the framework in response to AddDevice
+ call from the PnP manager.
+
+
+Arguments:
+
+ Driver - Handle to a framework driver object created in DriverEntry
+
+ DeviceInit - Pointer to a framework-allocated WDFDEVICE_INIT structure.
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+
+{
+ NTSTATUS status;
+ PSERIAL_DEVICE_EXTENSION pDevExt;
+ static ULONG currentInstance = 0;
+ WDF_FILEOBJECT_CONFIG fileobjectConfig;
+ WDFDEVICE device;
+ WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerCallbacks;
+ WDF_OBJECT_ATTRIBUTES attributes;
+ WDF_IO_QUEUE_CONFIG queueConfig;
+ WDFQUEUE defaultqueue;
+ ULONG isMulti;
+ PULONG countSoFar;
+ WDF_INTERRUPT_CONFIG interruptConfig;
+ PSERIAL_INTERRUPT_CONTEXT interruptContext;
+ ULONG relinquishPowerPolicy;
+
+ DECLARE_UNICODE_STRING_SIZE(deviceName, DEVICE_OBJECT_NAME_LENGTH);
+
+ PAGED_CODE();
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP, "-->SerialEvtDeviceAdd\n");
+
+ status = RtlUnicodeStringPrintf(&deviceName, L"%ws%d",
+ L"\\Device\\Serial",
+ currentInstance++);
+
+
+ if (!NT_SUCCESS(status)) {
+ return status;
+ }
+
+ status = WdfDeviceInitAssignName(DeviceInit,& deviceName);
+ if (!NT_SUCCESS(status)) {
+ return status;
+ }
+
+ WdfDeviceInitSetExclusive(DeviceInit, TRUE);
+ WdfDeviceInitSetDeviceType(DeviceInit, FILE_DEVICE_SERIAL_PORT);
+
+ WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, REQUEST_CONTEXT);
+
+ WdfDeviceInitSetRequestAttributes(DeviceInit, &attributes);
+
+ //
+ // Zero out the PnpPowerCallbacks structure.
+ //
+ WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks);
+
+ //
+ // Set Callbacks for any of the functions we are interested in.
+ // If no callback is set, Framework will take the default action
+ // by itself. These next two callbacks set up and tear down hardware state,
+ // specifically that which only has to be done once.
+ //
+
+ pnpPowerCallbacks.EvtDevicePrepareHardware = SerialEvtPrepareHardware;
+ pnpPowerCallbacks.EvtDeviceReleaseHardware = SerialEvtReleaseHardware;
+
+ //
+ // These two callbacks set up and tear down hardware state that must be
+ // done every time the device moves in and out of the D0-working state.
+ //
+
+ pnpPowerCallbacks.EvtDeviceD0Entry = SerialEvtDeviceD0Entry;
+ pnpPowerCallbacks.EvtDeviceD0Exit = SerialEvtDeviceD0Exit;
+
+ //
+ // Specify the callback for monitoring when the device's interrupt are
+ // enabled or about to be disabled.
+ //
+
+ pnpPowerCallbacks.EvtDeviceD0EntryPostInterruptsEnabled = SerialEvtDeviceD0EntryPostInterruptsEnabled;
+ pnpPowerCallbacks.EvtDeviceD0ExitPreInterruptsDisabled = SerialEvtDeviceD0ExitPreInterruptsDisabled;
+
+ //
+ // Register the PnP and power callbacks.
+ //
+ WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks);
+
+ if ( !NT_SUCCESS(status)) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP,
+ "WdfDeviceInitSetPnpPowerEventCallbacks failed %!STATUS!\n",
+ status);
+ return status;
+ }
+
+ //
+ // Find out if we own power policy
+ //
+ SerialGetFdoRegistryKeyValue( DeviceInit,
+ L"SerialRelinquishPowerPolicy",
+ &relinquishPowerPolicy );
+
+ if(relinquishPowerPolicy) {
+ //
+ // FDO's are assumed to be power policy owner by default. So tell
+ // the framework explicitly to relinquish the power policy ownership.
+ //
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP,
+ "RelinquishPowerPolicy due to registry settings\n");
+
+ WdfDeviceInitSetPowerPolicyOwnership(DeviceInit, FALSE);
+ }
+
+ //
+ // For Windows XP and below, we will register for the WDM Preprocess callback
+ // for IRP_MJ_CREATE. This is done because, the Serenum filter doesn't handle
+ // creates that are marked pending. Since framework always marks the IRP pending,
+ // we are registering this WDM preprocess handler so that we can bypass the
+ // framework and handle the create and close ourself. This workaround is need
+ // only if you intend to install the Serenum as an upper filter.
+ //
+ if (RtlIsNtDdiVersionAvailable(NTDDI_VISTA) == FALSE) {
+
+ status = WdfDeviceInitAssignWdmIrpPreprocessCallback(
+ DeviceInit,
+ SerialWdmDeviceFileCreate,
+ IRP_MJ_CREATE,
+ NULL, // pointer minor function table
+ 0); // number of entries in the table
+
+ if (!NT_SUCCESS(status)) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP,
+ "WdfDeviceInitAssignWdmIrpPreprocessCallback failed %!STATUS!\n",
+ status);
+ return status;
+ }
+
+ status = WdfDeviceInitAssignWdmIrpPreprocessCallback(
+ DeviceInit,
+ SerialWdmFileClose,
+ IRP_MJ_CLOSE,
+ NULL, // pointer minor function table
+ 0); // number of entries in the table
+
+ if (!NT_SUCCESS(status)) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP,
+ "WdfDeviceInitAssignWdmIrpPreprocessCallback failed %!STATUS!\n",
+ status);
+ return status;
+ }
+
+ } else {
+
+ //
+ // FileEvents can opt for Device level synchronization only if the ExecutionLevel
+ // of the Device is passive. Since we can't choose passive execution-level for
+ // device because we have chose to synchronize timers & dpcs with the device,
+ // we will opt out of synchonization with the device for fileobjects.
+ // Note: If the driver has to synchronize Create with the other I/O events,
+ // it can create a queue and configure-dispatch create requests to the queue.
+ //
+ WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
+ attributes.SynchronizationScope = WdfSynchronizationScopeNone;
+
+ //
+ // Set Entry points for Create and Close..
+ //
+ WDF_FILEOBJECT_CONFIG_INIT(
+ &fileobjectConfig,
+ SerialEvtDeviceFileCreate,
+ SerialEvtFileClose,
+ WDF_NO_EVENT_CALLBACK // Cleanup
+ );
+
+ WdfDeviceInitSetFileObjectConfig(
+ DeviceInit,
+ &fileobjectConfig,
+ &attributes
+ );
+ }
+
+
+ //
+ // Since framework queues doesn't handle IRP_MJ_FLUSH_BUFFERS,
+ // IRP_MJ_QUERY_INFORMATION and IRP_MJ_SET_INFORMATION requests,
+ // we will register a preprocess callback to handle them.
+ //
+ status = WdfDeviceInitAssignWdmIrpPreprocessCallback(
+ DeviceInit,
+ SerialFlush,
+ IRP_MJ_FLUSH_BUFFERS,
+ NULL, // pointer minor function table
+ 0); // number of entries in the table
+
+ if (!NT_SUCCESS(status)) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP,
+ "WdfDeviceInitAssignWdmIrpPreprocessCallback failed %!STATUS!\n",
+ status);
+ return status;
+ }
+
+ status = WdfDeviceInitAssignWdmIrpPreprocessCallback(
+ DeviceInit,
+ SerialQueryInformationFile,
+ IRP_MJ_QUERY_INFORMATION,
+ NULL, // pointer minor function table
+ 0); // number of entries in the table
+
+ if (!NT_SUCCESS(status)) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP,
+ "WdfDeviceInitAssignWdmIrpPreprocessCallback failed %!STATUS!\n",
+ status);
+ return status;
+ }
+ status = WdfDeviceInitAssignWdmIrpPreprocessCallback(
+ DeviceInit,
+ SerialSetInformationFile,
+ IRP_MJ_SET_INFORMATION,
+ NULL, // pointer minor function table
+ 0); // number of entries in the table
+
+ if (!NT_SUCCESS(status)) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP,
+ "WdfDeviceInitAssignWdmIrpPreprocessCallback failed %!STATUS!\n",
+ status);
+ return status;
+ }
+
+
+ //
+ // Create a device
+ //
+ WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE (&attributes,
+ SERIAL_DEVICE_EXTENSION);
+ //
+ // Provide a callback to cleanup the context. This will be called
+ // when the device is removed.
+ //
+ attributes.EvtCleanupCallback = SerialEvtDeviceContextCleanup;
+ //
+ // By opting for SynchronizationScopeDevice, we tell the framework to
+ // synchronize callbacks events of all the objects directly associated
+ // with the device. In this driver, we will associate queues, dpcs,
+ // and timers. By doing that we don't have to worrry about synchronizing
+ // access to device-context by Io Events, cancel-routine, timer and dpc
+ // callbacks.
+ //
+ attributes.SynchronizationScope = WdfSynchronizationScopeDevice;
+
+ status = WdfDeviceCreate(&DeviceInit, &attributes, &device);
+ if (!NT_SUCCESS(status)) {
+
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP,
+ "SerialAddDevice - WdfDeviceCreate failed %!STATUS!\n",
+ status);
+ return status;
+ }
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP,
+ "Created device (%p) %wZ\n", device, &deviceName);
+
+ pDevExt = SerialGetDeviceExtension (device);
+
+ pDevExt->DriverObject = WdfDriverWdmGetDriverObject(Driver);
+
+ //
+ // This sample doesn't support multiport serial devices.
+ // Multiport devices allow other pseudo-serial devices with extra
+ // resources to specify another range of I/O ports.
+ //
+ if(!SerialGetRegistryKeyValue(device, L"MultiportDevice", &isMulti)) {
+ isMulti = 0;
+ }
+
+ if(isMulti) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP,
+ "This sample doesn't support multiport devices\n");
+ return STATUS_DEVICE_CONFIGURATION_ERROR;
+ }
+
+ //
+ // Set up the device extension.
+ //
+
+ pDevExt = SerialGetDeviceExtension (device);
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP,
+ "AddDevice PDO(0x%p) FDO(0x%p), Lower(0x%p) DevExt (0x%p)\n",
+ WdfDeviceWdmGetPhysicalDevice (device),
+ WdfDeviceWdmGetDeviceObject (device),
+ WdfDeviceWdmGetAttachedDevice(device),
+ pDevExt);
+
+ pDevExt->DeviceIsOpened = FALSE;
+ pDevExt->DeviceObject = WdfDeviceWdmGetDeviceObject(device);
+ pDevExt->WdfDevice = device;
+
+ pDevExt->TxFifoAmount = driverDefaults.TxFIFODefault;
+ pDevExt->UartRemovalDetect = driverDefaults.UartRemovalDetect;
+ pDevExt->CreatedSymbolicLink = FALSE;
+ pDevExt->OwnsPowerPolicy = relinquishPowerPolicy ? FALSE : TRUE;
+
+ status = SerialSetPowerPolicy(pDevExt);
+ if(!NT_SUCCESS(status)){
+ return status;
+ }
+
+ //
+ // We create four manual queues below.
+ // Read Queue..(how about using serial queue for read). Since requests
+ // jump from queue to queue, we cannot configure the queues to receive a
+ // particular type of request. For example, some of the IOCTLs end up
+ // in read and write queue.
+ //
+ WDF_IO_QUEUE_CONFIG_INIT(&queueConfig,
+ WdfIoQueueDispatchManual);
+
+ queueConfig.EvtIoStop = SerialEvtIoStop;
+ queueConfig.EvtIoResume = SerialEvtIoResume;
+ queueConfig.EvtIoCanceledOnQueue = SerialEvtCanceledOnQueue;
+
+ status = WdfIoQueueCreate (device,
+ &queueConfig,
+ WDF_NO_OBJECT_ATTRIBUTES,
+ &pDevExt->ReadQueue
+ );
+
+ if (!NT_SUCCESS(status)) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, " WdfIoQueueCreate for Read failed %!STATUS!\n", status);
+ return status;
+ }
+
+ //
+ // Write Queue..
+ //
+ WDF_IO_QUEUE_CONFIG_INIT(&queueConfig,
+ WdfIoQueueDispatchManual);
+
+ queueConfig.EvtIoStop = SerialEvtIoStop;
+ queueConfig.EvtIoResume = SerialEvtIoResume;
+ queueConfig.EvtIoCanceledOnQueue = SerialEvtCanceledOnQueue;
+
+ status = WdfIoQueueCreate (device,
+ &queueConfig,
+ WDF_NO_OBJECT_ATTRIBUTES,
+ &pDevExt->WriteQueue
+ );
+
+ if (!NT_SUCCESS(status)) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, " WdfIoQueueCreate for Write failed %!STATUS!\n", status);
+ return status;
+ }
+
+ //
+ // Mask Queue...
+ //
+ WDF_IO_QUEUE_CONFIG_INIT(&queueConfig,
+ WdfIoQueueDispatchManual
+ );
+
+ queueConfig.EvtIoCanceledOnQueue = SerialEvtCanceledOnQueue;
+
+ queueConfig.EvtIoStop = SerialEvtIoStop;
+ queueConfig.EvtIoResume = SerialEvtIoResume;
+
+ status = WdfIoQueueCreate (device,
+ &queueConfig,
+ WDF_NO_OBJECT_ATTRIBUTES,
+ &pDevExt->MaskQueue
+ );
+
+ if (!NT_SUCCESS(status)) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, " WdfIoQueueCreate for Mask failed %!STATUS!\n", status);
+ return status;
+ }
+
+ //
+ // Purge Queue..
+ //
+ WDF_IO_QUEUE_CONFIG_INIT(&queueConfig,
+ WdfIoQueueDispatchManual
+ );
+
+ queueConfig.EvtIoCanceledOnQueue = SerialEvtCanceledOnQueue;
+
+ queueConfig.EvtIoStop = SerialEvtIoStop;
+ queueConfig.EvtIoResume = SerialEvtIoResume;
+
+ status = WdfIoQueueCreate (device,
+ &queueConfig,
+ WDF_NO_OBJECT_ATTRIBUTES,
+ &pDevExt->PurgeQueue
+ );
+
+ if (!NT_SUCCESS(status)) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, " WdfIoQueueCreate for Purge failed %!STATUS!\n", status);
+ return status;
+ }
+
+ //
+ // All the incoming I/O requests are routed to the default queue and dispatch to the
+ // appropriate callback events. These callback event will check to see if another
+ // request is currently active. If so then it will forward it to other manual queues.
+ // All the queues are auto managed by the framework in response to the PNP
+ // and Power events.
+ //
+ WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(
+ &queueConfig,
+ WdfIoQueueDispatchParallel
+ );
+ queueConfig.EvtIoRead = SerialEvtIoRead;
+ queueConfig.EvtIoWrite = SerialEvtIoWrite;
+ queueConfig.EvtIoDeviceControl = SerialEvtIoDeviceControl;
+ queueConfig.EvtIoInternalDeviceControl = SerialEvtIoInternalDeviceControl;
+ queueConfig.EvtIoCanceledOnQueue = SerialEvtCanceledOnQueue;
+
+ queueConfig.EvtIoStop = SerialEvtIoStop;
+ queueConfig.EvtIoResume = SerialEvtIoResume;
+
+ status = WdfIoQueueCreate(device,
+ &queueConfig,
+ WDF_NO_OBJECT_ATTRIBUTES,
+ &defaultqueue
+ );
+ if (!NT_SUCCESS(status)) {
+
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, "WdfIoQueueCreate failed %!STATUS!\n", status);
+ return status;
+ }
+
+ //
+ // Create WDFINTERRUPT object. Let us leave the ShareVector to default value and
+ // let the framework decide whether to share the interrupt or not based on the
+ // ShareDisposition provided by the bus driver in the resource descriptor.
+ //
+
+ WDF_INTERRUPT_CONFIG_INIT(&interruptConfig,
+ SerialISR,
+ NULL);
+
+ interruptConfig.EvtInterruptDisable = SerialEvtInterruptDisable;
+ interruptConfig.EvtInterruptEnable = SerialEvtInterruptEnable;
+
+ WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, SERIAL_INTERRUPT_CONTEXT);
+
+ status = WdfInterruptCreate(device,
+ &interruptConfig,
+ &attributes,
+ &pDevExt->WdfInterrupt);
+
+ if (!NT_SUCCESS(status)) {
+
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, "Couldn't create interrupt for %wZ\n",
+ &pDevExt->DeviceName);
+ return status;
+ }
+
+ //
+ // Interrupt state wait lock...
+ //
+ WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
+ attributes.ParentObject = pDevExt->WdfInterrupt;
+
+ interruptContext = SerialGetInterruptContext(pDevExt->WdfInterrupt);
+
+ status = WdfWaitLockCreate(&attributes,
+ &interruptContext->InterruptStateLock
+ );
+
+ if (!NT_SUCCESS(status)) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, " WdfWaitLockCreate for InterruptStateLock failed %!STATUS!\n", status);
+ return status;
+ }
+
+ //
+ // Set interrupt policy
+ //
+ SerialSetInterruptPolicy(pDevExt->WdfInterrupt);
+
+ //
+ // Timers and DPCs...
+ //
+ status = SerialCreateTimersAndDpcs(pDevExt);
+ if (!NT_SUCCESS(status)) {
+
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, "SerialCreateTimersAndDpcs failed %x\n", status);
+ return status;
+ }
+
+ //
+ // Register with WMI.
+ //
+ status = SerialWmiRegistration(device);
+ if(!NT_SUCCESS (status)) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, "SerialWmiRegistration failed %!STATUS!\n", status);
+ return status;
+
+ }
+
+ //
+ // Upto this point, if we fail, we don't have to worry about freeing any resource because
+ // framework will free all the objects.
+ //
+ //
+ // Do the external naming.
+ //
+
+ status = SerialDoExternalNaming(pDevExt);
+ if (!NT_SUCCESS(status)) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, "External Naming Failed - Status %!STATUS!\n",
+ status);
+ return status;
+ }
+
+ //
+ // Finally increment the global system configuration that keeps track of number of serial ports.
+ //
+ countSoFar = &IoGetConfigurationInformation()->SerialCount;
+ (*countSoFar)++;
+ pDevExt->IsSystemConfigInfoUpdated = TRUE;
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP, "<--SerialEvtDeviceAdd\n");
+
+ return status;
+
+}
+#pragma warning(push)
+#pragma warning(disable:28118) // this callback will run at IRQL=PASSIVE_LEVEL
+_Use_decl_annotations_
+VOID
+SerialEvtDeviceContextCleanup (
+ WDFOBJECT Device
+ )
+/*++
+
+Routine Description:
+
+ EvtDeviceContextCleanup event callback cleans up anything done in
+ EvtDeviceAdd, except those things that are automatically cleaned
+ up by the Framework.
+
+ In a driver derived from this sample, it's quite likely that this function could
+ be deleted.
+
+Arguments:
+
+ Device - Handle to a framework device object.
+
+Return Value:
+
+ VOID
+
+--*/
+{
+ PSERIAL_DEVICE_EXTENSION deviceExtension;
+ PULONG countSoFar;
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP, "--> SerialDeviceContextCleanup\n");
+
+ PAGED_CODE();
+
+ deviceExtension = SerialGetDeviceExtension (Device);
+
+ if (deviceExtension->InterruptReadBuffer != NULL) {
+ ExFreePool(deviceExtension->InterruptReadBuffer);
+ deviceExtension->InterruptReadBuffer = NULL;
+ }
+
+ //
+ // Update the global configuration count for serial device.
+ //
+ if(deviceExtension->IsSystemConfigInfoUpdated) {
+ countSoFar = &IoGetConfigurationInformation()->SerialCount;
+ (*countSoFar)--;
+ }
+
+ SerialUndoExternalNaming(deviceExtension);
+
+ return;
+}
+#pragma warning(pop) // enable 28118 again
+
+NTSTATUS
+SerialEvtPrepareHardware(
+ WDFDEVICE Device,
+ WDFCMRESLIST Resources,
+ WDFCMRESLIST ResourcesTranslated
+ )
+/*++
+
+Routine Description:
+
+ SerialEvtPrepareHardware event callback performs operations that are necessary
+ to make the device operational. The framework calls the driver's
+ SerialEvtPrepareHardware callback when the PnP manager sends an IRP_MN_START_DEVICE
+ request to the driver stack.
+
+Arguments:
+
+ Device - Handle to a framework device object.
+
+ Resources - Handle to a collection of framework resource objects.
+ This collection identifies the raw (bus-relative) hardware
+ resources that have been assigned to the device.
+
+ ResourcesTranslated - Handle to a collection of framework resource objects.
+ This collection identifies the translated (system-physical)
+ hardware resources that have been assigned to the device.
+ The resources appear from the CPU's point of view.
+ Use this list of resources to map I/O space and
+ device-accessible memory into virtual address space
+
+Return Value:
+
+ WDF status code
+
+--*/
+{
+ PSERIAL_DEVICE_EXTENSION pDevExt;
+ NTSTATUS status;
+ CONFIG_DATA config;
+ PCONFIG_DATA pConfig = &config;
+ ULONG defaultClockRate = 1843200;
+
+ PAGED_CODE();
+
+ SerialDbgPrintEx (TRACE_LEVEL_INFORMATION, DBG_PNP, "--> SerialEvtPrepareHardware\n");
+ //
+ // Get the Device Extension..
+ //
+ pDevExt = SerialGetDeviceExtension (Device);
+
+ RtlZeroMemory(pConfig, sizeof(CONFIG_DATA));
+
+ //
+ // Initialize a config data structure with default values for those that
+ // may not already be initialized.
+ //
+
+ pConfig->LogFifo = driverDefaults.LogFifoDefault;
+
+
+ //
+ // Get the hw resources for the device.
+ //
+
+ status = SerialMapHWResources(Device, Resources, ResourcesTranslated, pConfig);
+
+ if (!NT_SUCCESS(status)) {
+ goto End;
+ }
+
+ //
+ // Open the "Device Parameters" section of registry for this device and get parameters.
+ //
+
+ if(!SerialGetRegistryKeyValue (Device,
+ L"DisablePort",
+ &pConfig->DisablePort)){
+ pConfig->DisablePort = 0;
+ }
+
+ if(!SerialGetRegistryKeyValue (Device,
+ L"ForceFifoEnable",
+ &pConfig->ForceFifoEnable)){
+ pConfig->ForceFifoEnable = driverDefaults.ForceFifoEnableDefault;
+ }
+
+ if(!SerialGetRegistryKeyValue (Device,
+ L"RxFIFO",
+ &pConfig->RxFIFO)){
+ pConfig->RxFIFO = driverDefaults.RxFIFODefault;
+ }
+
+ if(!SerialGetRegistryKeyValue (Device,
+ L"TxFIFO",
+ &pConfig->TxFIFO)){
+ pConfig->TxFIFO = driverDefaults.TxFIFODefault;
+ }
+
+ if(!SerialGetRegistryKeyValue (Device,
+ L"Share System Interrupt",
+ &pConfig->PermitShare)){
+ pConfig->PermitShare = driverDefaults.PermitShareDefault;
+ }
+
+ if(!SerialGetRegistryKeyValue (Device,
+ L"ClockRate",
+ &pConfig->ClockRate)) {
+ pConfig->ClockRate = defaultClockRate;
+ }
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP, "Com Port ClockRate: %x\n",
+ pConfig->ClockRate);
+
+ if(!SerialGetRegistryKeyValue(Device,
+ L"TL16C550C Auto Flow Control",
+ &pConfig->TL16C550CAFC)){
+ pConfig->TL16C550CAFC = 0;
+ }
+
+ status = SerialInitController(pDevExt, pConfig);
+
+ if (NT_SUCCESS(status)) {
+ }
+End:
+
+ SerialDbgPrintEx (TRACE_LEVEL_INFORMATION, DBG_PNP, "<-- SerialEvtPrepareHardware 0x%x\n", status);
+
+ return status;
+}
+
+NTSTATUS
+SerialEvtReleaseHardware(
+ 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.
+ In this driver, we will not receive this callback when there is open handle to
+ the device. We explicitly tell the framework (WdfDeviceSetStaticStopRemove) to
+ fail stop and query-remove when handle is open.
+
+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.
+
+--*/
+{
+ PSERIAL_DEVICE_EXTENSION pDevExt;
+
+ UNREFERENCED_PARAMETER(ResourcesTranslated);
+
+ PAGED_CODE();
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP,
+ "--> SerialEvtReleaseHardware\n");
+
+ pDevExt = SerialGetDeviceExtension (Device);
+
+ //
+ // Reset and put the device into a known initial state before releasing the hw resources.
+ // In this driver we can recieve this callback only when there is no handle open because
+ // we tell the framework to disable stop by calling WdfDeviceSetStaticStopRemove.
+ // Since we have already reset the device in our close handler, we don't have to
+ // do anything other than unmapping the I/O resources.
+ //
+
+ //
+ // Unmap any Memory-Mapped registers. Disconnecting from the interrupt will
+ // be done automatically by the framework.
+ //
+ SerialUnmapHWResources(pDevExt);
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP,
+ "<-- SerialEvtReleaseHardware\n");
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+SerialEvtDeviceD0EntryPostInterruptsEnabled(
+ IN WDFDEVICE Device,
+ IN WDF_POWER_DEVICE_STATE PreviousState
+ )
+/*++
+
+Routine Description:
+
+ EvtDeviceD0EntryPostInterruptsEnabled is called by the framework after the
+ driver has enabled the device's hardware interrupts.
+
+ 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.
+
+ PreviousState - A WDF_POWER_DEVICE_STATE-typed enumerator that identifies
+ the previous device power state.
+
+Return Value:
+
+ NTSTATUS - Failures will be logged, but not acted on.
+
+--*/
+{
+ PSERIAL_DEVICE_EXTENSION extension = SerialGetDeviceExtension(Device);
+ PSERIAL_INTERRUPT_CONTEXT interruptContext = SerialGetInterruptContext(extension->WdfInterrupt);
+ WDF_INTERRUPT_INFO info;
+
+ UNREFERENCED_PARAMETER(PreviousState);
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP,
+ "--> SerialEvtDeviceD0EntryPostInterruptsEnabled\n");
+ //
+ // The following lines of code show how to call WdfInterruptGetInfo.
+ //
+ WDF_INTERRUPT_INFO_INIT(&info);
+ WdfInterruptGetInfo(extension->WdfInterrupt, &info);
+
+ WdfWaitLockAcquire(interruptContext->InterruptStateLock, NULL);
+ interruptContext->IsInterruptConnected = TRUE;
+ WdfWaitLockRelease(interruptContext->InterruptStateLock);
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+SerialEvtDeviceD0ExitPreInterruptsDisabled(
+ IN WDFDEVICE Device,
+ IN WDF_POWER_DEVICE_STATE TargetState
+ )
+/*++
+
+Routine Description:
+
+ EvtDeviceD0ExitPreInterruptsDisabled is called by the framework before the
+ driver disables the device's hardware interrupts.
+
+Arguments:
+
+ Device - Handle to a framework device object.
+
+ TargetState - A WDF_POWER_DEVICE_STATE-typed enumerator that identifies the
+ device power state that the device is about to enter.
+
+Return Value:
+
+ NTSTATUS - Failures will be logged, but not acted on.
+
+--*/
+{
+ PSERIAL_DEVICE_EXTENSION extension = SerialGetDeviceExtension(Device);
+ PSERIAL_INTERRUPT_CONTEXT interruptContext = SerialGetInterruptContext(extension->WdfInterrupt);
+
+ UNREFERENCED_PARAMETER(TargetState);
+ PAGED_CODE();
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP,
+ "--> SerialEvtDeviceD0ExitPreInterruptsDisabled\n");
+
+ WdfWaitLockAcquire(interruptContext->InterruptStateLock, NULL);
+ interruptContext->IsInterruptConnected = FALSE;
+ WdfWaitLockRelease(interruptContext->InterruptStateLock);
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+SerialSetPowerPolicy(
+ IN PSERIAL_DEVICE_EXTENSION DeviceExtension
+ )
+{
+ WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS idleSettings;
+ //WDF_POWER_POLICY_EVENT_CALLBACKS powerPolicyCallbacks;
+ NTSTATUS status = STATUS_SUCCESS;
+ WDFDEVICE hDevice = DeviceExtension->WdfDevice;
+ ULONG powerOnClose;
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP,
+ "--> SerialSetPowerPolicy\n");
+
+ PAGED_CODE();
+
+ //
+ // Find out whether we want to power down the device when there no handles open.
+ //
+ SerialGetRegistryKeyValue(hDevice, L"EnablePowerManagement", &powerOnClose);
+ DeviceExtension->RetainPowerOnClose = powerOnClose ? TRUE : FALSE;
+
+ //
+ // In some drivers, the device must be specifically programmed to enable
+ // wake signals. UARTs were designed long, long before such a concept. So
+ // this driver, which just drives UARTs, doesn't register wake arm/disarm
+ // callbacks. Arming or disarming for UARTs has to be handled by side-band
+ // code that controls hardware designed more recently. In this case, ACPI
+ // is handling it. If one were to write a driver which implemented a more
+ // modern serial device, one might need to use these callbacks.
+ //
+
+ //
+ // Init the power policy callbacks
+ //
+ //WDF_POWER_POLICY_EVENT_CALLBACKS_INIT(&powerPolicyCallbacks);
+
+ //
+ // This group of three callbacks allows this sample driver to manage
+ // arming the device for wake from the S0 state.
+ //
+
+ //powerPolicyCallbacks.EvtDeviceArmWakeFromS0 = SerialEvtDeviceWakeArmS0;
+ //powerPolicyCallbacks.EvtDeviceDisarmWakeFromS0 = SerialEvtDeviceWakeDisarmS0;
+ //powerPolicyCallbacks.EvtDeviceWakeFromS0Triggered = SerialEvtDeviceWakeTriggeredS0;
+
+ //
+ // This group of three callbacks allows the device to be armed for wake
+ // from Sx (S1, S2, S3 or S4.) Networking devices can optionally be put
+ // into a state where a packet sent to them will cause the device's wake
+ // signal to be triggered, which causes the machine to wake, moving back
+ // into the S0 state.
+ //
+
+ //powerPolicyCallbacks.EvtDeviceArmWakeFromSx = SerialEvtDeviceWakeArmSx;
+ //powerPolicyCallbacks.EvtDeviceDisarmWakeFromSx = SerialEvtDeviceWakeDisarmSx;
+ //powerPolicyCallbacks.EvtDeviceWakeFromSxTriggered = SerialEvtDeviceWakeTriggeredSx;
+
+ //
+ // Register the power policy callbacks.
+ //
+ //WdfDeviceSetPowerPolicyEventCallbacks(hDevice, &powerPolicyCallbacks);
+
+ //
+ // Init the idle policy structure. By setting IdleCannotWakeFromS0 we tell the framework
+ // to power down the device without arming for wake. The only way the device can come
+ // back to D0 is when we call WdfDeviceStopIdle in SerialEvtDeviceFileCreate.
+ // We can't choose IdleCanWakeFromS0 by default is because onboard serial ports typically
+ // don't have wake capability. If the driver is used for plugin boards that does support
+ // wait-wake, you can update the settings to match that. If MS provided modem driver
+ // is used on ports that does support wake on ring, then it will update the settings
+ // by sending an internal ioctl to us.
+ //
+ WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT(&idleSettings, IdleCannotWakeFromS0);
+ if(DeviceExtension->OwnsPowerPolicy && !DeviceExtension->RetainPowerOnClose) {
+ //
+ // Since we don't have to retain power when there are no open handles, we
+ // register for idle power management to save power. Check the use of
+ // WdfDeviceStopIdle in SerialEvtDeviceFileCreate.
+ //
+ idleSettings.UserControlOfIdleSettings = IdleAllowUserControl;
+
+ status = WdfDeviceAssignS0IdleSettings(hDevice, &idleSettings);
+ if ( !NT_SUCCESS(status)) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP,
+ "WdfDeviceSetPowerPolicyS0IdlePolicy failed %x \n", status);
+ return status;
+ }
+ }
+
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP, "<-- SerialSetPowerPolicy\n");
+
+ return status;
+}
+
+UINT32
+SerialReportMaxBaudRate(ULONG Bauds)
+/*++
+
+Routine Description:
+
+ This routine returns the max baud rate given a selection of rates
+
+Arguments:
+
+ Bauds - Bit-encoded list of supported bauds
+
+
+ Return Value:
+
+ The max baud rate listed in Bauds
+
+--*/
+{
+ int i;
+
+ PAGED_CODE();
+
+ for(i=0; SupportedBaudRates[i].BaudRate != SERIAL_BAUD_INVALID; i++) {
+
+ if(Bauds & SupportedBaudRates[i].Mask) {
+ return SupportedBaudRates[i].BaudRate;
+ }
+ }
+
+ //
+ // We're in bad shape
+ //
+
+ return 0;
+}
+
+NTSTATUS
+SerialInitController(
+ IN PSERIAL_DEVICE_EXTENSION pDevExt,
+ IN PCONFIG_DATA PConfigData
+ )
+/*++
+
+Routine Description:
+
+ Really too many things to mention here. In general initializes
+ kernel synchronization structures, allocates the typeahead buffer,
+ sets up defaults, etc.
+
+Arguments:
+
+ PDevObj - Device object for the device to be started
+
+ PConfigData - Pointer to a record for a single port.
+
+Return Value:
+
+ STATUS_SUCCCESS if everything went ok. A !NT_SUCCESS status
+ otherwise.
+
+--*/
+
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ SHORT junk;
+ int i;
+
+ PAGED_CODE();
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP, "--> SerialInitController for %wZ\n",
+ &pDevExt->DeviceName);
+
+ //
+ // Save the value of clock input to the part. We use this to calculate
+ // the divisor latch value. The value is in Hertz.
+ //
+
+ pDevExt->ClockRate = PConfigData->ClockRate;
+
+
+ //
+ // Save if we have to enable TI's auto flow control
+ //
+
+
+ pDevExt->TL16C550CAFC = PConfigData->TL16C550CAFC;
+
+
+ //
+ // Map the memory for the control registers for the serial device
+ // into virtual memory.
+ //
+ pDevExt->Controller =
+ SerialGetMappedAddress(PConfigData->TrController,
+ PConfigData->SpanOfController,
+ (BOOLEAN)PConfigData->AddressSpace,
+ &pDevExt->UnMapRegisters);
+
+
+ if (!pDevExt->Controller) {
+
+ SerialLogError(
+ pDevExt->DriverObject,
+ pDevExt->DeviceObject,
+ PConfigData->TrController,
+ SerialPhysicalZero,
+ 0,
+ 0,
+ 0,
+ 7,
+ STATUS_SUCCESS,
+ SERIAL_REGISTERS_NOT_MAPPED,
+ pDevExt->DeviceName.Length+sizeof(WCHAR),
+ pDevExt->DeviceName.Buffer,
+ 0,
+ NULL
+ );
+
+ SerialDbgPrintEx(TRACE_LEVEL_WARNING, DBG_PNP, "Could not map memory for device "
+ "registers for %wZ\n", &pDevExt->DeviceName);
+
+ pDevExt->UnMapRegisters = FALSE;
+ status = STATUS_NONE_MAPPED;
+ goto ExtensionCleanup;
+
+ }
+
+ pDevExt->AddressSpace = PConfigData->AddressSpace;
+ pDevExt->SpanOfController = PConfigData->SpanOfController;
+
+ //
+ // Save off the interface type and the bus number.
+ //
+
+ pDevExt->Vector = PConfigData->TrVector;
+ pDevExt->Irql = (UCHAR)PConfigData->TrIrql;
+ pDevExt->InterruptMode = PConfigData->InterruptMode;
+ pDevExt->Affinity = PConfigData->Affinity;
+
+ //
+ // If the user said to permit sharing within the device, propagate this
+ // through.
+ //
+
+ pDevExt->PermitShare = PConfigData->PermitShare;
+
+
+ //
+ // Before we test whether the port exists (which will enable the FIFO)
+ // convert the rx trigger value to what should be used in the register.
+ //
+ // If a bogus value was given - crank them down to 1.
+ //
+ // If this is a "souped up" UART with like a 64 byte FIFO, they
+ // should use the appropriate "spoofing" value to get the desired
+ // results. I.e., if on their chip 0xC0 in the FCR is for 64 bytes,
+ // they should specify 14 in the registry.
+ //
+
+ switch (PConfigData->RxFIFO) {
+
+ case 1:
+
+ pDevExt->RxFifoTrigger = SERIAL_1_BYTE_HIGH_WATER;
+ break;
+
+ case 4:
+
+ pDevExt->RxFifoTrigger = SERIAL_4_BYTE_HIGH_WATER;
+ break;
+
+ case 8:
+
+ pDevExt->RxFifoTrigger = SERIAL_8_BYTE_HIGH_WATER;
+ break;
+
+ case 14:
+
+ pDevExt->RxFifoTrigger = SERIAL_14_BYTE_HIGH_WATER;
+ break;
+
+ default:
+
+ pDevExt->RxFifoTrigger = SERIAL_1_BYTE_HIGH_WATER;
+ break;
+
+ }
+
+
+ if (PConfigData->TxFIFO < 1) {
+
+ pDevExt->TxFifoAmount = 1;
+
+ } else {
+
+ pDevExt->TxFifoAmount = PConfigData->TxFIFO;
+
+ }
+
+ if (!SerialDoesPortExist(
+ pDevExt,
+ &pDevExt->DeviceName,
+ PConfigData->ForceFifoEnable,
+ PConfigData->LogFifo
+ )) {
+
+ //
+ // We couldn't verify that there was actually a
+ // port. No need to log an error as the port exist
+ // code will log exactly why.
+ //
+
+ SerialDbgPrintEx(TRACE_LEVEL_WARNING, DBG_PNP, "DoesPortExist test failed for "
+ "%wZ\n", &pDevExt->DeviceName);
+
+ status = STATUS_NO_SUCH_DEVICE;
+ goto ExtensionCleanup;
+
+ }
+
+
+ //
+ // If the user requested that we disable the port, then
+ // do it now. Log the fact that the port has been disabled.
+ //
+
+ if (PConfigData->DisablePort) {
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP, "disabled port %wZ as requested in "
+ "configuration\n", &pDevExt->DeviceName);
+
+ status = STATUS_NO_SUCH_DEVICE;
+
+ SerialLogError(
+ pDevExt->DriverObject,
+ pDevExt->DeviceObject,
+ PConfigData->TrController,
+ SerialPhysicalZero,
+ 0,
+ 0,
+ 0,
+ 57,
+ STATUS_SUCCESS,
+ SERIAL_DISABLED_PORT,
+ pDevExt->DeviceName.Length+sizeof(WCHAR),
+ pDevExt->DeviceName.Buffer,
+ 0,
+ NULL
+ );
+
+ goto ExtensionCleanup;
+
+ }
+
+
+
+ //
+ // Set up the default device control fields.
+ // Note that if the values are changed after
+ // the file is open, they do NOT revert back
+ // to the old value at file close.
+ //
+
+ pDevExt->SpecialChars.XonChar = SERIAL_DEF_XON;
+ pDevExt->SpecialChars.XoffChar = SERIAL_DEF_XOFF;
+ pDevExt->HandFlow.ControlHandShake = SERIAL_DTR_CONTROL;
+ pDevExt->HandFlow.FlowReplace = SERIAL_RTS_CONTROL;
+
+
+ //
+ // Default Line control protocol. 7E1
+ //
+ // Seven data bits.
+ // Even parity.
+ // 1 Stop bits.
+ //
+
+ pDevExt->LineControl = SERIAL_7_DATA |
+ SERIAL_EVEN_PARITY |
+ SERIAL_NONE_PARITY;
+
+ pDevExt->ValidDataMask = 0x7f;
+ pDevExt->CurrentBaud = 1200;
+
+
+ //
+ // We set up the default xon/xoff limits.
+ //
+ // This may be a bogus value. It looks like the BufferSize
+ // is not set up until the device is actually opened.
+ //
+
+ pDevExt->HandFlow.XoffLimit = pDevExt->BufferSize >> 3;
+ pDevExt->HandFlow.XonLimit = pDevExt->BufferSize >> 1;
+
+ pDevExt->BufferSizePt8 = ((3*(pDevExt->BufferSize>>2))+
+ (pDevExt->BufferSize>>4));
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP, " The default interrupt read buffer size is: %d\n"
+ "------ The XoffLimit is : %d\n"
+ "------ The XonLimit is : %d\n"
+ "------ The pt 8 size is : %d\n",
+ pDevExt->BufferSize, pDevExt->HandFlow.XoffLimit,
+ pDevExt->HandFlow.XonLimit, pDevExt->BufferSizePt8);
+
+
+ //
+ // Go through all the "named" baud rates to find out which ones
+ // can be supported with this port.
+ //
+ //
+
+ pDevExt->SupportedBauds = SERIAL_BAUD_USER;
+
+
+ for(i=0; SupportedBaudRates[i].BaudRate != SERIAL_BAUD_INVALID; i++) {
+
+ if (!NT_ERROR(SerialGetDivisorFromBaud(
+ pDevExt->ClockRate,
+ (LONG)SupportedBaudRates[i].BaudRate,
+ &junk
+ ))) {
+
+ pDevExt->SupportedBauds |= SupportedBaudRates[i].Mask;
+ }
+ }
+
+
+
+
+ //
+ // Mark this device as not being opened by anyone. We keep a
+ // variable around so that spurious interrupts are easily
+ // dismissed by the ISR.
+ //
+
+ SetDeviceIsOpened(pDevExt, FALSE, FALSE);
+
+ //
+ // Store values into the extension for interval timing.
+ //
+
+ //
+ // If the interval timer is less than a second then come
+ // in with a short "polling" loop.
+ //
+ // For large (> then 2 seconds) use a 1 second poller.
+ //
+
+ pDevExt->ShortIntervalAmount.QuadPart = -1;
+ pDevExt->LongIntervalAmount.QuadPart = -10000000;
+ pDevExt->CutOverAmount.QuadPart = 200000000;
+
+ DISABLE_ALL_INTERRUPTS (pDevExt, pDevExt->Controller);
+
+ WRITE_MODEM_CONTROL(pDevExt, pDevExt->Controller, (UCHAR)0);
+
+ // make sure there is no escape character currently set
+ pDevExt->EscapeChar = 0;
+ //
+ // This should set up everything as it should be when
+ // a device is to be opened. We do need to lower the
+ // modem lines, and disable the recalcitrant fifo
+ // so that it will show up if the user boots to dos.
+ //
+
+#pragma prefast(suppress: __WARNING_IRQ_SET_TOO_HIGH, "This warning is because we are calling interrupt synchronize routine directly. Suppress it because interrupt is not connected yet.")
+#pragma prefast(suppress:6387, "Interrupt is UNREFERENCED_PARAMETER, so it can be NULL")
+ SerialReset(NULL, pDevExt);
+
+#pragma prefast(suppress: __WARNING_IRQ_SET_TOO_HIGH, "This warning is because we are calling interrupt synchronize routine directly. Suppress it because interrupt is not connected yet.")
+#pragma prefast(suppress:6387, "Interrupt is UNREFERENCED_PARAMETER, so it can be NULL")
+ SerialMarkClose(NULL, pDevExt);
+
+#pragma prefast(suppress: __WARNING_IRQ_SET_TOO_HIGH, "This warning is because we are calling interrupt synchronize routine directly. Suppress it because interrupt is not connected yet.")
+#pragma prefast(suppress:6387, "Interrupt is UNREFERENCED_PARAMETER, so it can be NULL")
+ SerialClrRTS(NULL, pDevExt);
+
+#pragma prefast(suppress: __WARNING_IRQ_SET_TOO_HIGH, "This warning is because we are calling interrupt synchronize routine directly. Suppress it because interrupt is not connected yet.")
+#pragma prefast(suppress:6387, "Interrupt is UNREFERENCED_PARAMETER, so it can be NULL")
+ SerialClrDTR(NULL, pDevExt);
+
+ //
+ // Fill in WMI hardware data
+ //
+ pDevExt->WmiHwData.IrqNumber = pDevExt->Irql;
+ pDevExt->WmiHwData.IrqLevel = pDevExt->Irql;
+ pDevExt->WmiHwData.IrqVector = pDevExt->Vector;
+ pDevExt->WmiHwData.IrqAffinityMask = pDevExt->Affinity;
+ pDevExt->WmiHwData.InterruptType = pDevExt->InterruptMode == Latched
+ ? SERIAL_WMI_INTTYPE_LATCHED : SERIAL_WMI_INTTYPE_LEVEL;
+ pDevExt->WmiHwData.BaseIOAddress = (ULONG_PTR)pDevExt->Controller;
+
+ //
+ // Fill in WMI device state data (as defaults)
+ //
+
+ pDevExt->WmiCommData.BaudRate = pDevExt->CurrentBaud;
+ pDevExt->WmiCommData.BitsPerByte = (pDevExt->LineControl & 0x03) + 5;
+ pDevExt->WmiCommData.ParityCheckEnable = (pDevExt->LineControl & 0x08)
+ ? TRUE : FALSE;
+
+ switch (pDevExt->LineControl & SERIAL_PARITY_MASK) {
+ case SERIAL_NONE_PARITY:
+ pDevExt->WmiCommData.Parity = SERIAL_WMI_PARITY_NONE;
+ break;
+
+ case SERIAL_ODD_PARITY:
+ pDevExt->WmiCommData.Parity = SERIAL_WMI_PARITY_ODD;
+ break;
+
+ case SERIAL_EVEN_PARITY:
+ pDevExt->WmiCommData.Parity = SERIAL_WMI_PARITY_EVEN;
+ break;
+
+ case SERIAL_MARK_PARITY:
+ pDevExt->WmiCommData.Parity = SERIAL_WMI_PARITY_MARK;
+ break;
+
+ case SERIAL_SPACE_PARITY:
+ pDevExt->WmiCommData.Parity = SERIAL_WMI_PARITY_SPACE;
+ break;
+
+ default:
+ ASSERTMSG(0, "Illegal Parity setting for WMI");
+ pDevExt->WmiCommData.Parity = SERIAL_WMI_PARITY_NONE;
+ break;
+ }
+
+ pDevExt->WmiCommData.StopBits = pDevExt->LineControl & SERIAL_STOP_MASK
+ ? (pDevExt->WmiCommData.BitsPerByte == 5 ? SERIAL_WMI_STOP_1_5
+ : SERIAL_WMI_STOP_2) : SERIAL_WMI_STOP_1;
+ pDevExt->WmiCommData.XoffCharacter = pDevExt->SpecialChars.XoffChar;
+ pDevExt->WmiCommData.XoffXmitThreshold = pDevExt->HandFlow.XoffLimit;
+ pDevExt->WmiCommData.XonCharacter = pDevExt->SpecialChars.XonChar;
+ pDevExt->WmiCommData.XonXmitThreshold = pDevExt->HandFlow.XonLimit;
+ pDevExt->WmiCommData.MaximumBaudRate
+ = SerialReportMaxBaudRate(pDevExt->SupportedBauds);
+ pDevExt->WmiCommData.MaximumOutputBufferSize = (UINT32)((ULONG)-1);
+ pDevExt->WmiCommData.MaximumInputBufferSize = (UINT32)((ULONG)-1);
+ pDevExt->WmiCommData.Support16BitMode = FALSE;
+ pDevExt->WmiCommData.SupportDTRDSR = TRUE;
+ pDevExt->WmiCommData.SupportIntervalTimeouts = TRUE;
+ pDevExt->WmiCommData.SupportParityCheck = TRUE;
+ pDevExt->WmiCommData.SupportRTSCTS = TRUE;
+ pDevExt->WmiCommData.SupportXonXoff = TRUE;
+ pDevExt->WmiCommData.SettableBaudRate = TRUE;
+ pDevExt->WmiCommData.SettableDataBits = TRUE;
+ pDevExt->WmiCommData.SettableFlowControl = TRUE;
+ pDevExt->WmiCommData.SettableParity = TRUE;
+ pDevExt->WmiCommData.SettableParityCheck = TRUE;
+ pDevExt->WmiCommData.SettableStopBits = TRUE;
+ pDevExt->WmiCommData.IsBusy = FALSE;
+
+ //
+ // Common error path cleanup. If the status is
+ // bad, get rid of the device extension, device object
+ // and any memory associated with it.
+ //
+
+ExtensionCleanup: ;
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP, "<-- SerialInitController %x\n", status);
+
+ return status;
+}
+
+
+NTSTATUS
+SerialMapHWResources(
+ IN WDFDEVICE Device,
+ IN WDFCMRESLIST PResList,
+ IN WDFCMRESLIST PTrResList,
+ OUT PCONFIG_DATA PConfig
+ )
+/*++
+
+Routine Description:
+
+ This routine will get the configuration information and put
+ it and the translated values into CONFIG_DATA structures.
+
+Arguments:
+
+ Device - Handle to a framework device object.
+
+ Resources - Handle to a collection of framework resource objects.
+ This collection identifies the raw (bus-relative) hardware
+ resources that have been assigned to the device.
+
+ ResourcesTranslated - Handle to a collection of framework resource objects.
+ This collection identifies the translated (system-physical)
+ hardware resources that have been assigned to the device.
+ The resources appear from the CPU's point of view.
+ Use this list of resources to map I/O space and
+ device-accessible memory into virtual address space
+
+Return Value:
+
+ STATUS_SUCCESS if consistant configuration was found - otherwise.
+ returns STATUS_SERIAL_NO_DEVICE_INITED.
+
+--*/
+
+{
+ PSERIAL_DEVICE_EXTENSION pDevExt;
+ NTSTATUS status = STATUS_SUCCESS;
+ ULONG i;
+ PCM_PARTIAL_RESOURCE_DESCRIPTOR pPartialTrResourceDesc, pPartialRawResourceDesc;
+ ULONG gotInt = 0;
+ ULONG gotIO = 0;
+ ULONG ioResIndex = 0;
+ ULONG curIoIndex = 0;
+ ULONG gotMem = 0;
+ BOOLEAN DebugPortInUse = FALSE;
+
+ PAGED_CODE();
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP, "--> SerialMapHWResources\n");
+
+ //
+ // Get the DeviceExtension..
+ //
+ pDevExt = SerialGetDeviceExtension (Device);
+
+ if ((PResList == NULL) || (PTrResList == NULL)) {
+ ASSERT(PResList != NULL);
+ ASSERT(PTrResList != NULL);
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ goto End;
+ }
+
+ for (i = 0; i < WdfCmResourceListGetCount(PTrResList); i++) {
+
+ pPartialTrResourceDesc = WdfCmResourceListGetDescriptor(PTrResList, i);
+ pPartialRawResourceDesc = WdfCmResourceListGetDescriptor(PResList, i);
+
+ switch (pPartialTrResourceDesc->Type) {
+ case CmResourceTypePort:
+
+ ASSERT(!(pPartialTrResourceDesc->u.Port.Length == SERIAL_STATUS_LENGTH));
+
+ if (gotIO == 0) {
+
+ if (curIoIndex == ioResIndex) {
+
+ gotIO = 1;
+ PConfig->TrController = pPartialTrResourceDesc->u.Port.Start;
+
+ if (!PConfig->TrController.LowPart) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, "Bogus port address %x\n",
+ PConfig->TrController.LowPart);
+ status = STATUS_DEVICE_CONFIGURATION_ERROR;
+ goto End;
+ }
+ //
+ // We need the raw address to check if the debugger is using the com port
+ //
+ PConfig->Controller = pPartialRawResourceDesc->u.Port.Start;
+ PConfig->AddressSpace = pPartialTrResourceDesc->Flags;
+ pDevExt->SerialReadUChar = SerialReadPortUChar;
+ pDevExt->SerialWriteUChar = SerialWritePortUChar;
+
+ } else {
+ curIoIndex++;
+ }
+ }
+
+ break;
+
+ //
+ // If this is 8 bytes long and we haven't found any I/O range,
+ // then this is probably a fancy-pants machine with memory replacing
+ // IO space
+ //
+ case CmResourceTypeMemory:
+
+ ASSERT(!(pPartialTrResourceDesc->u.Port.Length == SERIAL_STATUS_LENGTH));
+
+ if ((gotMem == 0) && (gotIO == 0)
+ && (pPartialTrResourceDesc->u.Memory.Length
+ == (SERIAL_REGISTER_SPAN + SERIAL_STATUS_LENGTH))) {
+ gotMem = 1;
+ PConfig->TrController = pPartialTrResourceDesc->u.Memory.Start;
+
+ if (!PConfig->TrController.LowPart) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, "Bogus I/O memory address %x\n",
+ PConfig->TrController.LowPart);
+ status = STATUS_DEVICE_CONFIGURATION_ERROR;
+ goto End;
+ }
+
+ PConfig->Controller = pPartialRawResourceDesc->u.Memory.Start;
+ PConfig->AddressSpace = CM_RESOURCE_PORT_MEMORY;
+ PConfig->SpanOfController = SERIAL_REGISTER_SPAN;
+ pDevExt->SerialReadUChar = SerialReadRegisterUChar;
+ pDevExt->SerialWriteUChar = SerialWriteRegisterUChar;
+ }
+ break;
+
+ case CmResourceTypeInterrupt:
+ if (gotInt == 0) {
+ gotInt = 1;
+ PConfig->TrVector = pPartialTrResourceDesc->u.Interrupt.Vector;
+
+ if (!PConfig->TrVector) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, "Bogus vector 0\n");
+ status = STATUS_DEVICE_CONFIGURATION_ERROR;
+ goto End;
+ }
+
+ if (pPartialTrResourceDesc->ShareDisposition == CmResourceShareShared) {
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP, "Sharing interrupt with other devices \n");
+ } else {
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP, "Interrupt is not shared with other devices\n");
+ }
+
+ PConfig->TrIrql = pPartialTrResourceDesc->u.Interrupt.Level;
+ PConfig->Affinity = pPartialTrResourceDesc->u.Interrupt.Affinity;
+ }
+ break;
+
+ default: break;
+ } // switch (pPartialTrResourceDesc->Type)
+
+ } // for (i = 0; i < WdfCollectionGetCount
+
+ if(!((gotMem || gotIO) && gotInt) )
+ {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ goto End;
+ }
+
+ //
+ // First check what type of AddressSpace this port is in. Then check
+ // if the debugger is using this port. If it is, set DebugPortInUse to TRUE.
+ //
+ if(PConfig->AddressSpace == CM_RESOURCE_PORT_MEMORY) {
+
+ PHYSICAL_ADDRESS KdComPhysical;
+
+ KdComPhysical = MmGetPhysicalAddress(*KdComPortInUse);
+
+ if(KdComPhysical.LowPart == PConfig->Controller.LowPart) {
+ DebugPortInUse = TRUE;
+ }
+
+ } else {
+ //
+ // This compare is done using **untranslated** values since that is what
+ // the kernel shoves in regardless of the architecture.
+ //
+
+ if ((*KdComPortInUse) == (ULongToPtr(PConfig->Controller.LowPart))) {
+ DebugPortInUse = TRUE;
+ }
+ }
+
+ if (DebugPortInUse) {
+
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, "Kernel debugger is using port at "
+ "address %p\n", *KdComPortInUse);
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, "Serial driver will not load port\n");
+
+ SerialLogError(
+ pDevExt->DriverObject,
+ NULL,
+ PConfig->TrController,
+ SerialPhysicalZero,
+ 0,
+ 0,
+ 0,
+ 3,
+ STATUS_SUCCESS,
+ SERIAL_KERNEL_DEBUGGER_ACTIVE,
+ pDevExt->DeviceName.Length+sizeof(WCHAR),
+ pDevExt->DeviceName.Buffer,
+ 0,
+ NULL
+ );
+
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ goto End;
+ }
+
+End:
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP, "<-- SerialMapHWResources %x\n", status);
+
+ return status;
+}
+
+VOID
+SerialUnmapHWResources(
+ IN PSERIAL_DEVICE_EXTENSION PDevExt
+ )
+/*++
+
+Routine Description:
+
+ Releases resources (not pool) stored in the device extension.
+
+Arguments:
+
+ PDevExt - Pointer to the device extension to release resources from.
+
+Return Value:
+
+ VOID
+
+--*/
+{
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP, "-->SerialUnMapResources(%p)\n",
+ PDevExt);
+ PAGED_CODE();
+
+ //
+ // If necessary, unmap the device registers.
+ //
+
+ if (PDevExt->UnMapRegisters) {
+ MmUnmapIoSpace(PDevExt->Controller, PDevExt->SpanOfController);
+ PDevExt->UnMapRegisters = FALSE;
+ }
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP, "<--SerialUnMapResources\n");
+}
+
+
+NTSTATUS
+SerialReadSymName(
+ IN WDFDEVICE Device,
+ _Out_writes_bytes_(*SizeOfRegName) PWSTR RegName,
+ _Inout_ PUSHORT SizeOfRegName
+ )
+{
+ NTSTATUS status;
+ WDFKEY hKey;
+ UNICODE_STRING value;
+ UNICODE_STRING valueName;
+ USHORT requiredLength;
+
+ PAGED_CODE();
+
+ value.Buffer = RegName;
+ value.MaximumLength = *SizeOfRegName;
+ value.Length = 0;
+
+ status = WdfDeviceOpenRegistryKey(Device,
+ PLUGPLAY_REGKEY_DEVICE,
+ STANDARD_RIGHTS_ALL,
+ WDF_NO_OBJECT_ATTRIBUTES,
+ &hKey);
+
+ if (NT_SUCCESS (status)) {
+ //
+ // Fetch PortName which contains the suggested REG_SZ symbolic name.
+ //
+
+
+ RtlInitUnicodeString(&valueName, L"PortName");
+
+ status = WdfRegistryQueryUnicodeString (hKey,
+ &valueName,
+ &requiredLength,
+ &value);
+
+ if (!NT_SUCCESS (status)) {
+ //
+ // This is for PCMCIA which currently puts the name under Identifier.
+ //
+
+ RtlInitUnicodeString(&valueName, L"Identifier");
+ status = WdfRegistryQueryUnicodeString (hKey,
+ &valueName,
+ &requiredLength,
+ &value);
+
+ if (!NT_SUCCESS(status)) {
+ //
+ // Hmm. Either we have to pick a name or bail...
+ //
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, "Getting PortName/Identifier failed - %x\n", status);
+ }
+ }
+
+ WdfRegistryClose(hKey);
+ }
+
+ if(NT_SUCCESS(status)) {
+ //
+ // NULL terminate the string and return number of characters in the string.
+ //
+ if(value.Length > *SizeOfRegName - sizeof(WCHAR)) {
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ *SizeOfRegName = value.Length;
+ RegName[*SizeOfRegName/sizeof(WCHAR)] = UNICODE_NULL;
+ }
+ return status;
+}
+
+
+NTSTATUS
+SerialDoExternalNaming(IN PSERIAL_DEVICE_EXTENSION PDevExt)
+
+/*++
+
+Routine Description:
+
+ This routine will be used to create a symbolic link
+ to the driver name in the given object directory.
+
+ It will also create an entry in the device map for
+ this device - IF we could create the symbolic link.
+
+Arguments:
+
+ Extension - Pointer to the device extension.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ WCHAR pRegName[SYMBOLIC_NAME_LENGTH];
+ USHORT nameSize = sizeof(pRegName);
+ WDFSTRING stringHandle = NULL;
+ WDF_OBJECT_ATTRIBUTES attributes;
+ DECLARE_UNICODE_STRING_SIZE(symbolicLinkName,SYMBOLIC_NAME_LENGTH ) ;
+
+ PAGED_CODE();
+
+ WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
+ attributes.ParentObject = PDevExt->WdfDevice;
+ status = WdfStringCreate(NULL, &attributes, &stringHandle);
+ if(!NT_SUCCESS(status)){
+ goto SerialDoExternalNamingError;
+ }
+
+ status = WdfDeviceRetrieveDeviceName(PDevExt->WdfDevice, stringHandle);
+ if(!NT_SUCCESS(status)){
+ goto SerialDoExternalNamingError;
+ }
+
+ //
+ // Since we are storing the buffer pointer of the string handle in our
+ // extension, we will hold onto string handle until the device is deleted.
+ //
+ WdfStringGetUnicodeString(stringHandle, &PDevExt->DeviceName);
+
+ SerialGetRegistryKeyValue(PDevExt->WdfDevice, L"SerialSkipExternalNaming", &PDevExt->SkipNaming);
+
+ if (PDevExt->SkipNaming) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, "Skipping external naming due to registry settings\n");
+ return STATUS_SUCCESS;
+ }
+
+ status = SerialReadSymName(PDevExt->WdfDevice, pRegName, &nameSize);
+ if (!NT_SUCCESS(status)) {
+ goto SerialDoExternalNamingError;
+ }
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP, "DosName is %ws\n", pRegName);
+
+ status = RtlUnicodeStringPrintf(&symbolicLinkName,
+ L"%ws%ws",
+ L"\\DosDevices\\",
+ pRegName);
+
+ if (!NT_SUCCESS(status)) {
+ goto SerialDoExternalNamingError;
+ }
+
+ status = WdfDeviceCreateSymbolicLink(PDevExt->WdfDevice, &symbolicLinkName);
+
+ if (!NT_SUCCESS(status)) {
+
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, "Couldn't create the symbolic link for port %wZ\n", &symbolicLinkName);
+
+ goto SerialDoExternalNamingError;
+
+ }
+
+
+ PDevExt->CreatedSymbolicLink = TRUE;
+
+ status = RtlWriteRegistryValue(RTL_REGISTRY_DEVICEMAP, SERIAL_DEVICE_MAP,
+ PDevExt->DeviceName.Buffer,
+ REG_SZ,
+ pRegName,
+ nameSize);
+
+ if (!NT_SUCCESS(status)) {
+
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, "Couldn't create the device map entry\n"
+ "------- for port %ws\n", PDevExt->DeviceName.Buffer);
+
+ goto SerialDoExternalNamingError;
+ }
+
+ PDevExt->CreatedSerialCommEntry = TRUE;
+
+ //
+ // Make the device visible via a device association as well.
+ // The reference string is the eight digit device index
+ //
+ status = WdfDeviceCreateDeviceInterface(PDevExt->WdfDevice,
+ (LPGUID) &GUID_DEVINTERFACE_COMPORT,
+ NULL);
+
+ if (!NT_SUCCESS (status)) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, "Couldn't register class association\n"
+ "for port %wZ\n", &PDevExt->DeviceName);
+
+ goto SerialDoExternalNamingError;
+ }
+
+ return status;
+
+ SerialDoExternalNamingError:;
+
+ //
+ // Clean up error conditions
+ //
+
+ PDevExt->DeviceName.Buffer = NULL;
+
+ if (PDevExt->CreatedSerialCommEntry) {
+ _Analysis_assume_(NULL != PDevExt->DeviceName.Buffer);
+ RtlDeleteRegistryValue(RTL_REGISTRY_DEVICEMAP, SERIAL_DEVICE_MAP,
+ PDevExt->DeviceName.Buffer);
+ }
+
+ if(stringHandle) {
+ WdfObjectDelete(stringHandle);
+ }
+
+ return status;
+}
+
+
+VOID
+SerialUndoExternalNaming(IN PSERIAL_DEVICE_EXTENSION Extension)
+
+/*++
+
+Routine Description:
+
+ This routine will be used to delete a symbolic link
+ to the driver name in the given object directory.
+
+ It will also delete an entry in the device map for
+ this device if the symbolic link had been created.
+
+Arguments:
+
+ Extension - Pointer to the device extension.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ NTSTATUS status;
+ PWCHAR deviceName = Extension->DeviceName.Buffer;
+
+ PAGED_CODE();
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP,
+ "In SerialUndoExternalNaming for extension: "
+ "%p of port %ws\n", Extension, deviceName);
+
+ //
+ // Maybe there is nothing for us to do
+ //
+
+ if (Extension->SkipNaming) {
+ return;
+ }
+
+ //
+ // We're cleaning up here. One reason we're cleaning up
+ // is that we couldn't allocate space for the NtNameOfPort.
+ //
+
+ if ((deviceName != NULL) && Extension->CreatedSerialCommEntry) {
+
+ status = RtlDeleteRegistryValue(RTL_REGISTRY_DEVICEMAP,
+ SERIAL_DEVICE_MAP,
+ deviceName);
+ if (!NT_SUCCESS(status)) {
+
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP,
+ "Couldn't delete value entry %ws\n",
+ deviceName);
+
+ }
+ }
+}
+
+VOID
+SerialPurgePendingRequests(PSERIAL_DEVICE_EXTENSION pDevExt)
+/*++
+
+Routine Description:
+
+ This routine completes any irps pending for the passed device object.
+
+Arguments:
+
+ PDevObj - Pointer to the device object whose irps must die.
+
+Return Value:
+
+ VOID
+
+--*/
+{
+ NTSTATUS status;
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP,
+ ">SerialPurgePendingRequests(%p)\n", pDevExt);
+
+ //
+ // Then cancel all the reads and writes.
+ //
+
+ SerialPurgeRequests(pDevExt->WriteQueue, &pDevExt->CurrentWriteRequest);
+
+ SerialPurgeRequests(pDevExt->ReadQueue, &pDevExt->CurrentReadRequest);
+
+ //
+ // Next get rid of purges.
+ //
+
+ SerialPurgeRequests(pDevExt->PurgeQueue, &pDevExt->CurrentPurgeRequest);
+
+ //
+ // Get rid of any mask operations.
+ //
+
+ SerialPurgeRequests( pDevExt->MaskQueue, &pDevExt->CurrentMaskRequest);
+
+ //
+ // Now get rid of pending wait mask request.
+ //
+
+ if (pDevExt->CurrentWaitRequest) {
+
+ status = SerialClearCancelRoutine(pDevExt->CurrentWaitRequest, TRUE );
+ if (NT_SUCCESS(status)) {
+
+ SerialCompleteRequest(pDevExt->CurrentWaitRequest, STATUS_CANCELLED, 0);
+ pDevExt->CurrentWaitRequest = NULL;
+
+ }
+
+ }
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP, "<SerialPurgePendingRequests\n");
+}
+
+BOOLEAN
+SerialDoesPortExist(
+ IN PSERIAL_DEVICE_EXTENSION Extension,
+ IN PUNICODE_STRING InsertString,
+ IN ULONG ForceFifo,
+ IN ULONG LogFifo
+ )
+
+/*++
+
+Routine Description:
+
+ This routine examines several of what might be the serial device
+ registers. It ensures that the bits that should be zero are zero.
+
+ In addition, this routine will determine if the device supports
+ fifo's. If it does it will enable the fifo's and turn on a boolean
+ in the extension that indicates the fifo's presence.
+
+ NOTE: If there is indeed a serial port at the address specified
+ it will absolutely have interrupts inhibited upon return
+ from this routine.
+
+ NOTE: Since this routine should be called fairly early in
+ the device driver initialization, the only element
+ that needs to be filled in is the base register address.
+
+ NOTE: These tests all assume that this code is the only
+ code that is looking at these ports or this memory.
+
+ This is a not to unreasonable assumption even on
+ multiprocessor systems.
+
+Arguments:
+
+ Extension - A pointer to a serial device extension.
+ InsertString - String to place in an error log entry.
+ ForceFifo - !0 forces the fifo to be left on if found.
+ LogFifo - !0 forces a log message if fifo found.
+
+Return Value:
+
+ Will return true if the port really exists, otherwise it
+ will return false.
+
+--*/
+
+{
+
+
+ UCHAR regContents;
+ BOOLEAN returnValue = TRUE;
+ UCHAR oldIERContents;
+ UCHAR oldLCRContents;
+ USHORT value1;
+ USHORT value2;
+ KIRQL oldIrql;
+
+ //
+ // Save of the line control.
+ //
+
+ oldLCRContents = READ_LINE_CONTROL(Extension, Extension->Controller);
+
+ //
+ // Make sure that we are *aren't* accessing the divsior latch.
+ //
+
+ WRITE_LINE_CONTROL(Extension,
+ Extension->Controller,
+ (UCHAR)(oldLCRContents & ~SERIAL_LCR_DLAB)
+ );
+
+ oldIERContents = READ_INTERRUPT_ENABLE(Extension, Extension->Controller);
+
+ //
+ // Go up to power level for a very short time to prevent
+ // any interrupts from this device from coming in.
+ //
+
+ KeRaiseIrql(
+ POWER_LEVEL,
+ &oldIrql
+ );
+
+ WRITE_INTERRUPT_ENABLE(Extension,
+ Extension->Controller,
+ 0x0f
+ );
+
+ value1 = READ_INTERRUPT_ENABLE(Extension, Extension->Controller);
+ value1 = value1 << 8;
+ value1 |= READ_RECEIVE_BUFFER(Extension, Extension->Controller);
+
+ READ_DIVISOR_LATCH(Extension,
+ Extension->Controller,
+ (PSHORT) &value2
+ );
+
+ WRITE_LINE_CONTROL(Extension,
+ Extension->Controller,
+ oldLCRContents
+ );
+
+ //
+ // Put the ier back to where it was before. If we are on a
+ // level sensitive port this should prevent the interrupts
+ // from coming in. If we are on a latched, we don't care
+ // cause the interrupts generated will just get dropped.
+ //
+
+ WRITE_INTERRUPT_ENABLE(Extension,
+ Extension->Controller,
+ oldIERContents
+ );
+
+ KeLowerIrql(oldIrql);
+
+ if (value1 == value2) {
+
+ SerialLogError(
+ Extension->DeviceObject->DriverObject,
+ Extension->DeviceObject,
+ SerialPhysicalZero,
+ SerialPhysicalZero,
+ 0,
+ 0,
+ 0,
+ 62,
+ STATUS_SUCCESS,
+ SERIAL_DLAB_INVALID,
+ InsertString->Length+sizeof(WCHAR),
+ InsertString->Buffer,
+ 0,
+ NULL
+ );
+ returnValue = FALSE;
+ goto AllDone;
+
+ }
+
+ AllDone: ;
+
+
+ //
+ // If we think that there is a serial device then we determine
+ // if a fifo is present.
+ //
+
+ if (returnValue) {
+
+ //
+ // Well, we think it's a serial device. Absolutely
+ // positively, prevent interrupts from occuring.
+ //
+ // We disable all the interrupt enable bits, and
+ // push down all the lines in the modem control
+ // We only needed to push down OUT2 which in
+ // PC's must also be enabled to get an interrupt.
+ //
+
+ DISABLE_ALL_INTERRUPTS(Extension, Extension->Controller);
+
+ WRITE_MODEM_CONTROL(Extension, Extension->Controller, (UCHAR)0);
+
+ //
+ // See if this is a 16550. We do this by writing to
+ // what would be the fifo control register with a bit
+ // pattern that tells the device to enable fifo's.
+ // We then read the iterrupt Id register to see if the
+ // bit pattern is present that identifies the 16550.
+ //
+
+ WRITE_FIFO_CONTROL(Extension,
+ Extension->Controller,
+ SERIAL_FCR_ENABLE
+ );
+
+ regContents = READ_INTERRUPT_ID_REG(Extension, Extension->Controller);
+
+ if (regContents & SERIAL_IIR_FIFOS_ENABLED) {
+
+ //
+ // Save off that the device supports fifos.
+ //
+
+ Extension->FifoPresent = TRUE;
+
+ //
+ // There is a fine new "super" IO chip out there that
+ // will get stuck with a line status interrupt if you
+ // attempt to clear the fifo and enable it at the same
+ // time if data is present. The best workaround seems
+ // to be that you should turn off the fifo read a single
+ // byte, and then re-enable the fifo.
+ //
+
+ WRITE_FIFO_CONTROL(Extension,
+ Extension->Controller,
+ (UCHAR)0
+ );
+
+ READ_RECEIVE_BUFFER(Extension, Extension->Controller);
+
+ //
+ // There are fifos on this card. Set the value of the
+ // receive fifo to interrupt when 4 characters are present.
+ //
+
+ WRITE_FIFO_CONTROL(Extension, Extension->Controller,
+ (UCHAR)(SERIAL_FCR_ENABLE
+ | Extension->RxFifoTrigger
+ | SERIAL_FCR_RCVR_RESET
+ | SERIAL_FCR_TXMT_RESET));
+
+ }
+
+ //
+ // The !Extension->FifoPresent is included in the test so that
+ // broken chips like the WinBond will still work after we test
+ // for the fifo.
+ //
+
+ if (!ForceFifo || !Extension->FifoPresent) {
+
+ Extension->FifoPresent = FALSE;
+ WRITE_FIFO_CONTROL(Extension,
+ Extension->Controller,
+ (UCHAR)0
+ );
+
+ }
+
+ if (Extension->FifoPresent) {
+
+ if (LogFifo) {
+
+ SerialLogError(
+ Extension->DeviceObject->DriverObject,
+ Extension->DeviceObject,
+ SerialPhysicalZero,
+ SerialPhysicalZero,
+ 0,
+ 0,
+ 0,
+ 15,
+ STATUS_SUCCESS,
+ SERIAL_FIFO_PRESENT,
+ InsertString->Length+sizeof(WCHAR),
+ InsertString->Buffer,
+ 0,
+ NULL
+ );
+
+ }
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP,
+ "Fifo's detected at port address: %p\n",
+ Extension->Controller);
+ }
+ }
+
+ return returnValue;
+}
+
+
+
+BOOLEAN
+SerialReset(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This places the hardware in a standard configuration.
+
+ NOTE: This assumes that it is called at interrupt level.
+
+
+Arguments:
+
+ Context - The device extension for serial device
+ being managed.
+
+Return Value:
+
+ Always FALSE.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION extension = Context;
+ UCHAR regContents;
+ UCHAR oldModemControl;
+ ULONG i;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ //
+ // Adjust the out2 bit.
+ // This will also prevent any interrupts from occuring.
+ //
+
+ oldModemControl = READ_MODEM_CONTROL(extension, extension->Controller);
+
+ WRITE_MODEM_CONTROL(extension, extension->Controller,
+ (UCHAR)(oldModemControl & ~SERIAL_MCR_OUT2));
+
+ //
+ // Reset the fifo's if there are any.
+ //
+
+ if (extension->FifoPresent) {
+
+ //
+ // There is a fine new "super" IO chip out there that
+ // will get stuck with a line status interrupt if you
+ // attempt to clear the fifo and enable it at the same
+ // time if data is present. The best workaround seems
+ // to be that you should turn off the fifo read a single
+ // byte, and then re-enable the fifo.
+ //
+
+ WRITE_FIFO_CONTROL(extension,
+ extension->Controller,
+ (UCHAR)0
+ );
+
+ READ_RECEIVE_BUFFER(extension, extension->Controller);
+
+ WRITE_FIFO_CONTROL(extension,
+ extension->Controller,
+ (UCHAR)(SERIAL_FCR_ENABLE | extension->RxFifoTrigger |
+ SERIAL_FCR_RCVR_RESET | SERIAL_FCR_TXMT_RESET)
+ );
+
+ }
+
+ //
+ // Make sure that the line control set up correct.
+ //
+ // 1) Make sure that the Divisor latch select is set
+ // up to select the transmit and receive register.
+ //
+ // 2) Make sure that we aren't in a break state.
+ //
+
+ regContents = READ_LINE_CONTROL(extension, extension->Controller);
+ regContents &= ~(SERIAL_LCR_DLAB | SERIAL_LCR_BREAK);
+
+ WRITE_LINE_CONTROL(extension,
+ extension->Controller,
+ regContents
+ );
+
+ //
+ // Read the receive buffer until the line status is
+ // clear. (Actually give up after a 5 reads.)
+ //
+
+ for (i = 0;
+ i < 5;
+ i++
+ ) {
+ #pragma warning(disable: 4127)
+ if (IsNotNEC_98) {
+ #pragma warning(default: 4127)
+ READ_RECEIVE_BUFFER(extension, extension->Controller);
+ if (!(READ_LINE_STATUS(extension, extension->Controller) & 1)) {
+
+ break;
+
+ }
+ } else {
+ //
+ // I get incorrect data when read enpty buffer.
+ // But do not read no data! for PC98!
+ //
+ if (!(READ_LINE_STATUS(extension, extension->Controller) & 1)) {
+
+ break;
+
+ }
+ READ_RECEIVE_BUFFER(extension, extension->Controller);
+ }
+
+ }
+
+ //
+ // Read the modem status until the low 4 bits are
+ // clear. (Actually give up after a 5 reads.)
+ //
+
+ for (i = 0;
+ i < 1000;
+ i++
+ ) {
+
+ if (!(READ_MODEM_STATUS(extension, extension->Controller) & 0x0f)) {
+
+ break;
+
+ }
+
+ }
+
+ //
+ // Now we set the line control, modem control, and the
+ // baud to what they should be.
+ //
+
+ //
+ // See if we have to enable special Auto Flow Control
+ //
+
+ if (extension->TL16C550CAFC) {
+ oldModemControl = READ_MODEM_CONTROL(extension, extension->Controller);
+
+ WRITE_MODEM_CONTROL(extension, extension->Controller,
+ (UCHAR)(oldModemControl | SERIAL_MCR_TL16C550CAFE));
+ }
+
+
+
+ SerialSetLineControl(extension->WdfInterrupt, extension);
+
+ SerialSetupNewHandFlow(
+ extension,
+ &extension->HandFlow
+ );
+
+ SerialHandleModemUpdate(
+ extension,
+ FALSE
+ );
+
+ {
+ SHORT appropriateDivisor;
+ SERIAL_IOCTL_SYNC s;
+
+ SerialGetDivisorFromBaud( extension->ClockRate,
+ extension->CurrentBaud,
+ &appropriateDivisor );
+
+ s.Extension = extension;
+ s.Data = (PVOID) (ULONG_PTR) appropriateDivisor;
+ SerialSetBaud(extension->WdfInterrupt, &s);
+ }
+
+ //
+ // Enable which interrupts we want to receive.
+ //
+ // NOTE NOTE: This does not actually let interrupts
+ // occur. We must still raise the OUT2 bit in the
+ // modem control register. We will do that on open.
+ //
+
+ ENABLE_ALL_INTERRUPTS(extension, extension->Controller);
+
+ //
+ // Read the interrupt id register until the low bit is
+ // set. (Actually give up after a 5 reads.)
+ //
+
+ for (i = 0;
+ i < 5;
+ i++
+ ) {
+
+ if (READ_INTERRUPT_ID_REG(extension, extension->Controller) & 0x01) {
+
+ break;
+
+ }
+
+ }
+
+ //
+ // Now we know that nothing could be transmitting at this point
+ // so we set the HoldingEmpty indicator.
+ //
+
+ extension->HoldingEmpty = TRUE;
+
+ return FALSE;
+}
+
+
+
+PVOID
+SerialGetMappedAddress(
+ PHYSICAL_ADDRESS IoAddress,
+ ULONG NumberOfBytes,
+ ULONG AddressSpace,
+ PBOOLEAN MappedAddress
+ )
+
+/*++
+
+Routine Description:
+
+ This routine maps an IO address to system address space.
+
+Arguments:
+
+ IoAddress - base device address to be mapped.
+ NumberOfBytes - number of bytes for which address is valid.
+ AddressSpace - Denotes whether the address is in io space or memory.
+ MappedAddress - indicates whether the address was mapped.
+ This only has meaning if the address returned
+ is non-null.
+
+Return Value:
+
+ Mapped address
+
+--*/
+
+{
+ PVOID address;
+
+ PAGED_CODE();
+
+ //
+ // Map the device base address into the virtual address space
+ // if the address is in memory space.
+ //
+
+ if (!AddressSpace) {
+
+ address = LocalMmMapIoSpace(IoAddress,
+ NumberOfBytes);
+
+ *MappedAddress = (BOOLEAN)((address)?(TRUE):(FALSE));
+
+
+ } else {
+
+ address = ULongToPtr(IoAddress.LowPart);
+ *MappedAddress = FALSE;
+
+ }
+
+ return address;
+}
+
+VOID
+SerialSetInterruptPolicy(
+ _In_ WDFINTERRUPT WdfInterrupt
+ )
+/*++
+
+Routine Description:
+
+ This routine shows how to set the interrupt policy preferences.
+
+Arguments:
+
+ WdfInterrupt - Interrupt object handle.
+
+Return Value:
+
+ None
+
+--*/
+{
+ WDF_INTERRUPT_EXTENDED_POLICY policyAndGroup;
+#ifdef SERIAL_SELECT_INTERRUPT_GROUP
+ USHORT groupCount = 1;
+ USHORT group = 0;
+ UNICODE_STRING funcName;
+ PFN_KE_GET_ACTIVE_GROUP_COUNT fnKeQueryActiveGroupCount;
+ PFN_KE_QUERY_GROUP_AFFINITY fnKeQueryGroupAffinity;
+ KAFFINITY groupAffinity = (KAFFINITY)1;
+#endif
+
+ WDF_INTERRUPT_EXTENDED_POLICY_INIT(&policyAndGroup);
+ policyAndGroup.Priority = WdfIrqPriorityNormal;
+
+#ifdef SERIAL_SELECT_INTERRUPT_GROUP
+ //
+ // If OS supports groups, find how many they are.
+ //
+ RtlInitUnicodeString(&funcName, L"KeQueryActiveGroupCount");
+ fnKeQueryActiveGroupCount = (PFN_KE_GET_ACTIVE_GROUP_COUNT)
+ MmGetSystemRoutineAddress(&funcName);
+
+ if (fnKeQueryActiveGroupCount != NULL) {
+ groupCount = fnKeQueryActiveGroupCount();
+
+ //
+ // Make sure there is at least one group for the boot processor.
+ //
+ if (0 == groupCount) {
+ groupCount = 1;
+ }
+ }
+
+ if (groupCount <= SERIAL_PREFERRED_INTERRUPT_GROUP) {
+ group = groupCount - 1;
+ }
+ else {
+ group = SERIAL_PREFERRED_INTERRUPT_GROUP;
+ }
+
+ //
+ // Get the group affinity.
+ //
+ RtlInitUnicodeString(&funcName, L"KeQueryGroupAffinity");
+ fnKeQueryGroupAffinity = (PFN_KE_QUERY_GROUP_AFFINITY)
+ MmGetSystemRoutineAddress(&funcName);
+
+ if (fnKeQueryGroupAffinity != NULL) {
+ groupAffinity = fnKeQueryGroupAffinity(group);
+
+ //
+ // Active groups have at least one processor.
+ //
+ if ((KAFFINITY)0 == groupAffinity) {
+ groupAffinity = (KAFFINITY)1;
+ }
+ }
+
+ //
+ // Initialize group.
+ //
+ policyAndGroup.Policy = WdfIrqPolicySpecifiedProcessors;
+ policyAndGroup.TargetProcessorSetAndGroup.Group = group;
+ policyAndGroup.TargetProcessorSetAndGroup.Mask = groupAffinity;
+#endif
+
+ //
+ // Set interrupt policy and group preference.
+ //
+ WdfInterruptSetExtendedPolicy(WdfInterrupt, &policyAndGroup);
+}
+
diff --git a/serial/serial/power.c b/serial/serial/power.c
new file mode 100644
index 00000000..1898504a
--- /dev/null
+++ b/serial/serial/power.c
@@ -0,0 +1,331 @@
+/*++
+
+Copyright (c) Microsoft Corporation
+
+Module Name:
+
+ power.c
+
+Abstract:
+
+ This module contains the code that handles the power IRPs for the serial
+ driver.
+
+Environment:
+
+ Kernel mode
+
+--*/
+
+#include "precomp.h"
+
+
+#if defined(EVENT_TRACING)
+#include "power.tmh"
+#endif
+
+
+PCHAR
+DbgDevicePowerString(
+ IN WDF_POWER_DEVICE_STATE Type
+ );
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGESER,SerialEvtDeviceD0Exit)
+#pragma alloc_text(PAGESER,SerialSaveDeviceState)
+#endif // ALLOC_PRAGMA
+
+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";
+ }
+}
+
+NTSTATUS
+SerialEvtDeviceD0Entry(
+ IN WDFDEVICE Device,
+ IN WDF_POWER_DEVICE_STATE PreviousState
+ )
+/*++
+
+Routine Description:
+
+ EvtDeviceD0Entry event callback must perform any operations that are
+ necessary before the specified device is used. It will be called every
+ time the hardware needs to be (re-)initialized. This includes after
+ IRP_MN_START_DEVICE, IRP_MN_CANCEL_STOP_DEVICE, IRP_MN_CANCEL_REMOVE_DEVICE,
+ IRP_MN_SET_POWER-D0.
+
+ This function is not marked pageable because this function is in the
+ device power up path. When a function is marked pagable and the code
+ section is paged out, it will generate a page fault which could impact
+ the fast resume behavior because the client driver will have to wait
+ until the system drivers can service this page fault.
+
+ This function runs at PASSIVE_LEVEL, even though it is not paged. A
+ driver can optionally make this function pageable if DO_POWER_PAGABLE
+ is set. Even if DO_POWER_PAGABLE isn't set, this function still runs
+ at PASSIVE_LEVEL. In this case, though, the function absolutely must
+ not do anything that will cause a page fault.
+
+Arguments:
+
+ Device - Handle to a framework device object.
+
+ PreviousState - Device power state which the device was in most recently.
+ If the device is being newly started, this will be
+ PowerDeviceUnspecified.
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+{
+ PSERIAL_DEVICE_EXTENSION deviceExtension;
+ PSERIAL_DEVICE_STATE pDevState;
+ SHORT divisor;
+ SERIAL_IOCTL_SYNC S;
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_POWER,
+ "-->SerialEvtDeviceD0Entry - coming from %s\n", DbgDevicePowerString(PreviousState));
+
+ deviceExtension = SerialGetDeviceExtension (Device);
+ pDevState = &deviceExtension->DeviceState;
+
+ //
+ // Restore the state of the UART. First, that involves disabling
+ // interrupts both via OUT2 and IER.
+ //
+
+ WRITE_MODEM_CONTROL(deviceExtension, deviceExtension->Controller, 0);
+ DISABLE_ALL_INTERRUPTS(deviceExtension, deviceExtension->Controller);
+
+ //
+ // Set the baud rate
+ //
+
+ SerialGetDivisorFromBaud(deviceExtension->ClockRate, deviceExtension->CurrentBaud, &divisor);
+ S.Extension = deviceExtension;
+ S.Data = (PVOID) (ULONG_PTR) divisor;
+
+#pragma prefast(suppress: __WARNING_INFERRED_IRQ_TOO_LOW, "PFD warning that we are calling interrupt synchronize routine directly. Suppress it because interrupt is disabled above.")
+ SerialSetBaud(deviceExtension->WdfInterrupt, &S);
+
+ //
+ // Reset / Re-enable the FIFO's
+ //
+
+ if (deviceExtension->FifoPresent) {
+ WRITE_FIFO_CONTROL(deviceExtension, deviceExtension->Controller, (UCHAR)0);
+ READ_RECEIVE_BUFFER(deviceExtension, deviceExtension->Controller);
+ WRITE_FIFO_CONTROL(deviceExtension, deviceExtension->Controller,
+ (UCHAR)(SERIAL_FCR_ENABLE | deviceExtension->RxFifoTrigger
+ | SERIAL_FCR_RCVR_RESET
+ | SERIAL_FCR_TXMT_RESET));
+ } else {
+ WRITE_FIFO_CONTROL(deviceExtension, deviceExtension->Controller, (UCHAR)0);
+ }
+
+ //
+ // Restore a couple more registers
+ //
+
+ WRITE_INTERRUPT_ENABLE(deviceExtension, deviceExtension->Controller, pDevState->IER);
+ WRITE_LINE_CONTROL(deviceExtension, deviceExtension->Controller, pDevState->LCR);
+
+ //
+ // Clear out any stale interrupts
+ //
+
+ READ_INTERRUPT_ID_REG(deviceExtension, deviceExtension->Controller);
+ READ_LINE_STATUS(deviceExtension, deviceExtension->Controller);
+ READ_MODEM_STATUS(deviceExtension, deviceExtension->Controller);
+
+ //
+ // TODO: move this code to EvtInterruptEnable.
+ //
+
+ if (deviceExtension->DeviceState.Reopen == TRUE) {
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_POWER, "Reopening device\n");
+
+ SetDeviceIsOpened(deviceExtension, TRUE, FALSE);
+
+ //
+ // This enables interrupts on the device!
+ //
+
+ WRITE_MODEM_CONTROL(deviceExtension, deviceExtension->Controller,
+ (UCHAR)(pDevState->MCR | SERIAL_MCR_OUT2));
+
+ //
+ // Refire the state machine
+ //
+
+ DISABLE_ALL_INTERRUPTS(deviceExtension, deviceExtension->Controller);
+ ENABLE_ALL_INTERRUPTS(deviceExtension, deviceExtension->Controller);
+ }
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_POWER, "<--SerialEvtDeviceD0Entry\n");
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+SerialEvtDeviceD0Exit(
+ IN WDFDEVICE Device,
+ IN WDF_POWER_DEVICE_STATE TargetState
+ )
+/*++
+
+Routine Description:
+
+ 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:
+
+ NTSTATUS
+
+--*/
+{
+ PSERIAL_DEVICE_EXTENSION deviceExtension;
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_POWER,
+ "-->SerialEvtDeviceD0Exit - moving to %s\n", DbgDevicePowerString(TargetState));
+
+ PAGED_CODE();
+
+ deviceExtension = SerialGetDeviceExtension (Device);
+
+ if (deviceExtension->DeviceIsOpened == TRUE) {
+ LARGE_INTEGER charTime;
+
+ SetDeviceIsOpened(deviceExtension, FALSE, TRUE);
+
+ charTime.QuadPart = -SerialGetCharTime(deviceExtension).QuadPart;
+
+ //
+ // Shut down the chip
+ //
+
+ SerialDisableUART(deviceExtension);
+
+ //
+ // Drain the device
+ //
+
+ SerialDrainUART(deviceExtension, &charTime);
+
+ //
+ // Save the device state
+ //
+
+ SerialSaveDeviceState(deviceExtension);
+ }
+ else
+ {
+ SetDeviceIsOpened(deviceExtension, FALSE, FALSE);
+ }
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_POWER, "<--SerialEvtDeviceD0Exit\n");
+
+ return STATUS_SUCCESS;
+}
+
+
+VOID
+SerialSaveDeviceState(IN PSERIAL_DEVICE_EXTENSION PDevExt)
+/*++
+
+Routine Description:
+
+ This routine saves the device state of the UART
+
+Arguments:
+
+ PDevExt - Pointer to the device extension for the devobj to save the state
+ for.
+
+Return Value:
+
+ VOID
+
+
+--*/
+{
+ PSERIAL_DEVICE_STATE pDevState = &PDevExt->DeviceState;
+
+ PAGED_CODE();
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_POWER, "Entering SerialSaveDeviceState\n");
+
+ //
+ // Read necessary registers direct
+ //
+
+ pDevState->IER = READ_INTERRUPT_ENABLE(PDevExt, PDevExt->Controller);
+ pDevState->MCR = READ_MODEM_CONTROL(PDevExt, PDevExt->Controller);
+ pDevState->LCR = READ_LINE_CONTROL(PDevExt, PDevExt->Controller);
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_POWER, "Leaving SerialSaveDeviceState\n");
+}
+
+
+VOID
+SetDeviceIsOpened(IN PSERIAL_DEVICE_EXTENSION PDevExt, IN BOOLEAN DeviceIsOpened, IN BOOLEAN Reopen)
+{
+
+ PDevExt->DeviceIsOpened = DeviceIsOpened;
+ PDevExt->DeviceState.Reopen = Reopen;
+
+}
+
+
+
diff --git a/serial/serial/precomp.h b/serial/serial/precomp.h
new file mode 100644
index 00000000..7a17a360
--- /dev/null
+++ b/serial/serial/precomp.h
@@ -0,0 +1,18 @@
+
+#include <stddef.h>
+#include <stdarg.h>
+#define WIN9X_COMPAT_SPINLOCK
+#include "ntddk.h"
+#include <wdf.h>
+#define NTSTRSAFE_LIB
+#include <ntstrsafe.h>
+#include "ntddser.h"
+#include <wmilib.h>
+#include <initguid.h> // required for GUID definitions
+#include <wmidata.h>
+#include "serial.h"
+#include "serialp.h"
+#include "serlog.h"
+#include "log.h"
+#include "trace.h"
+
diff --git a/serial/serial/precompsrc.c b/serial/serial/precompsrc.c
new file mode 100644
index 00000000..5944cf51
--- /dev/null
+++ b/serial/serial/precompsrc.c
@@ -0,0 +1 @@
+#include "precomp.h" \ No newline at end of file
diff --git a/serial/serial/purge.c b/serial/serial/purge.c
new file mode 100644
index 00000000..103aef9c
--- /dev/null
+++ b/serial/serial/purge.c
@@ -0,0 +1,175 @@
+/*++
+
+Copyright (c) Microsoft Corporation
+
+Module Name:
+
+ purge.c
+
+Abstract:
+
+ This module contains the code that is very specific to purge
+ operations in the serial driver
+
+Environment:
+
+ Kernel mode
+
+--*/
+
+#include "precomp.h"
+
+#if defined(EVENT_TRACING)
+#include "purge.tmh"
+#endif
+
+
+VOID
+SerialStartPurge(
+ IN PSERIAL_DEVICE_EXTENSION Extension
+ )
+
+/*++
+
+Routine Description:
+
+ Depending on the mask in the current request, purge the interrupt
+ buffer, the read queue, or the write queue, or all of the above.
+
+Arguments:
+
+ Extension - Pointer to the device extension.
+
+Return Value:
+
+ Will return STATUS_SUCCESS always. This is reasonable
+ since the DPC completion code that calls this routine doesn't
+ care and the purge request always goes through to completion
+ once it's started.
+
+--*/
+
+{
+
+ WDFREQUEST NewRequest;
+ PREQUEST_CONTEXT reqContext;
+
+ do {
+
+ ULONG Mask;
+ reqContext = SerialGetRequestContext(Extension->CurrentPurgeRequest);
+ Mask = *((ULONG *) (reqContext->SystemBuffer));
+
+ if (Mask & SERIAL_PURGE_TXABORT) {
+
+ SerialFlushRequests(
+ Extension->WriteQueue,
+ &Extension->CurrentWriteRequest
+ );
+
+ SerialFlushRequests(
+ Extension->WriteQueue,
+ &Extension->CurrentXoffRequest
+ );
+
+ }
+
+ if (Mask & SERIAL_PURGE_RXABORT) {
+
+ SerialFlushRequests(
+ Extension->ReadQueue,
+ &Extension->CurrentReadRequest
+ );
+
+ }
+
+ if (Mask & SERIAL_PURGE_RXCLEAR) {
+
+ //
+ // Clean out the interrupt buffer.
+ //
+ // Note that we do this under protection of the
+ // the drivers control lock so that we don't hose
+ // the pointers if there is currently a read that
+ // is reading out of the buffer.
+ //
+
+
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SerialPurgeInterruptBuff,
+ Extension
+ );
+
+ }
+
+ reqContext->Status = STATUS_SUCCESS;
+ reqContext->Information = 0;
+
+ SerialGetNextRequest(
+ &Extension->CurrentPurgeRequest,
+ Extension->PurgeQueue,
+ &NewRequest,
+ TRUE,
+ Extension
+ );
+
+ } while (NewRequest);
+
+ return;
+
+}
+
+BOOLEAN
+SerialPurgeInterruptBuff(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine simply resets the interrupt (typeahead) buffer.
+
+ NOTE: This routine is being called from WdfInterruptSynchronize.
+
+Arguments:
+
+ Context - Really a pointer to the device extension.
+
+Return Value:
+
+ Always false.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION Extension = Context;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ //
+ // The typeahead buffer is by definition empty if there
+ // currently is a read owned by the isr.
+ //
+
+
+ if (Extension->ReadBufferBase == Extension->InterruptReadBuffer) {
+
+ Extension->CurrentCharSlot = Extension->InterruptReadBuffer;
+ Extension->FirstReadableChar = Extension->InterruptReadBuffer;
+ Extension->LastCharSlot = Extension->InterruptReadBuffer +
+ (Extension->BufferSize - 1);
+ Extension->CharsInInterruptBuffer = 0;
+
+ SerialHandleReducedIntBuffer(Extension);
+
+ }
+
+ return FALSE;
+
+}
+
+
diff --git a/serial/serial/qsfile.c b/serial/serial/qsfile.c
new file mode 100644
index 00000000..b4f9d8d9
--- /dev/null
+++ b/serial/serial/qsfile.c
@@ -0,0 +1,180 @@
+/*++
+
+Copyright (c) 1991, 1992, 1993 - 1997 Microsoft Corporation
+
+Module Name:
+
+ qsfile.c
+
+Abstract:
+
+ This module contains the code that is very specific to query/set file
+ operations in the serial driver.
+
+Environment:
+
+ Kernel mode
+
+--*/
+
+#include "precomp.h"
+
+#if defined(EVENT_TRACING)
+#include "qsfile.tmh"
+#endif
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGESRP0,SerialQueryInformationFile)
+#pragma alloc_text(PAGESRP0,SerialSetInformationFile)
+#endif
+
+
+NTSTATUS
+SerialQueryInformationFile(
+ IN WDFDEVICE Device,
+ IN PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to query the end of file information on
+ the opened serial port. Any other file information request
+ is retured with an invalid parameter.
+
+ This routine always returns an end of file of 0.
+
+Arguments:
+
+ DeviceObject - Pointer to the device object for this device
+
+ Irp - Pointer to the IRP for the current request
+
+Return Value:
+
+ The function value is the final status of the call
+
+--*/
+
+{
+ NTSTATUS Status;
+ PIO_STACK_LOCATION IrpSp;
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP, ">SerialQueryInformationFile(%p, %p)\n", Device, Irp);
+
+ PAGED_CODE();
+
+
+ IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ Irp->IoStatus.Information = 0L;
+ Status = STATUS_SUCCESS;
+
+ if (IrpSp->Parameters.QueryFile.FileInformationClass ==
+ FileStandardInformation) {
+
+ if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength <
+ sizeof(FILE_STANDARD_INFORMATION))
+ {
+ Status = STATUS_BUFFER_TOO_SMALL;
+ }
+ else
+ {
+ PFILE_STANDARD_INFORMATION Buf = Irp->AssociatedIrp.SystemBuffer;
+
+ Buf->AllocationSize.QuadPart = 0;
+ Buf->EndOfFile = Buf->AllocationSize;
+ Buf->NumberOfLinks = 0;
+ Buf->DeletePending = FALSE;
+ Buf->Directory = FALSE;
+ Irp->IoStatus.Information = sizeof(FILE_STANDARD_INFORMATION);
+ }
+
+ } else if (IrpSp->Parameters.QueryFile.FileInformationClass ==
+ FilePositionInformation) {
+
+ if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength <
+ sizeof(FILE_POSITION_INFORMATION))
+ {
+ Status = STATUS_BUFFER_TOO_SMALL;
+ }
+ else
+ {
+
+ ((PFILE_POSITION_INFORMATION)Irp->AssociatedIrp.SystemBuffer)->
+ CurrentByteOffset.QuadPart = 0;
+ Irp->IoStatus.Information = sizeof(FILE_POSITION_INFORMATION);
+ }
+
+ } else {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+
+ Irp->IoStatus.Status = Status;
+
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ return Status;
+
+}
+
+NTSTATUS
+SerialSetInformationFile(
+ IN WDFDEVICE Device,
+ IN PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to set the end of file information on
+ the opened parallel port. Any other file information request
+ is retured with an invalid parameter.
+
+ This routine always ignores the actual end of file since
+ the query information code always returns an end of file of 0.
+
+Arguments:
+
+ DeviceObject - Pointer to the device object for this device
+
+ Irp - Pointer to the IRP for the current request
+
+Return Value:
+
+The function value is the final status of the call
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ PAGED_CODE();
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP, ">SerialSetInformationFile(%p, %p)\n", Device, Irp);
+
+ Irp->IoStatus.Information = 0L;
+ if ((IoGetCurrentIrpStackLocation(Irp)->
+ Parameters.SetFile.FileInformationClass ==
+ FileEndOfFileInformation) ||
+ (IoGetCurrentIrpStackLocation(Irp)->
+ Parameters.SetFile.FileInformationClass ==
+ FileAllocationInformation)) {
+
+ Status = STATUS_SUCCESS;
+
+ } else {
+
+ Status = STATUS_INVALID_PARAMETER;
+
+ }
+
+ Irp->IoStatus.Status = Status;
+
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ return Status;
+
+}
+
diff --git a/serial/serial/read.c b/serial/serial/read.c
new file mode 100644
index 00000000..a127d62f
--- /dev/null
+++ b/serial/serial/read.c
@@ -0,0 +1,1748 @@
+/*++
+
+Copyright (c) Microsoft Corporation
+
+Module Name:
+
+ read.c
+
+Abstract:
+
+ This module contains the code that is very specific to read
+ operations in the serial driver
+
+Environment:
+
+ Kernel mode
+
+--*/
+
+#include "precomp.h"
+
+#if defined(EVENT_TRACING)
+#include "read.tmh"
+#endif
+
+EVT_WDF_REQUEST_CANCEL SerialCancelCurrentRead;
+
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialGrabReadFromIsr;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialUpdateReadByIsr;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialUpdateInterruptBuffer;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialUpdateAndSwitchToUser;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialUpdateAndSwitchToNew;
+
+ULONG
+SerialGetCharsFromIntBuffer(
+ PSERIAL_DEVICE_EXTENSION Extension
+ );
+
+
+NTSTATUS
+SerialResizeBuffer(
+ IN PSERIAL_DEVICE_EXTENSION Extension
+ );
+
+ULONG
+SerialMoveToNewIntBuffer(
+ PSERIAL_DEVICE_EXTENSION Extension,
+ PUCHAR NewBuffer
+ );
+
+VOID
+SerialEvtIoRead(
+ IN WDFQUEUE Queue,
+ IN WDFREQUEST Request,
+ IN size_t Length
+ )
+
+/*++
+
+Routine Description:
+
+ This is the dispatch routine for reading. It validates the parameters
+ for the read request and if all is ok then it places the request
+ on the work queue.
+
+Arguments:
+
+ Queue - Queue handle
+ Request - Handle to the read 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:
+
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION extension;
+ NTSTATUS status;
+ WDFDEVICE hDevice;
+ WDF_REQUEST_PARAMETERS params;
+ PREQUEST_CONTEXT reqContext;
+ size_t bufLen;
+
+ hDevice = WdfIoQueueGetDevice(Queue);
+ extension = SerialGetDeviceExtension(hDevice);
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_READ,
+ ">SerialEvtIoRead(%p, 0x%I64x)\n", Request, Length);
+
+ if (SerialCompleteIfError(extension, Request) != STATUS_SUCCESS) {
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_READ, "<SerialEvtIoRead (2) %d\n", STATUS_CANCELLED);
+ return;
+
+ }
+
+ WDF_REQUEST_PARAMETERS_INIT(&params);
+
+ WdfRequestGetParameters(
+ Request,
+ &params
+ );
+
+ //
+ // Initialize the scratch area of the request.
+ //
+ reqContext = SerialGetRequestContext(Request);
+ reqContext->MajorFunction = params.Type;
+ reqContext->Length = (ULONG) Length;
+
+ status = WdfRequestRetrieveOutputBuffer (Request, Length, &reqContext->SystemBuffer, &bufLen);
+
+ if (!NT_SUCCESS (status)) {
+
+ SerialCompleteRequest(Request , status, 0);
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_READ, "<SerialEvtIoRead (5) %X\n", status);
+ return;
+ }
+
+ ASSERT(bufLen == reqContext->Length);
+
+ //
+ // Well it looks like we actually have to do some
+ // work. Put the read on the queue so that we can
+ // process it when our previous reads are done.
+ //
+ SerialStartOrQueue(extension, Request, extension->ReadQueue,
+ &extension->CurrentReadRequest, SerialStartRead);
+
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_READ, "<SerialEvtIoRead (3) %X\n", status);
+
+ return;
+
+}
+
+VOID
+SerialStartRead(
+ IN PSERIAL_DEVICE_EXTENSION Extension
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to start off any read. It initializes
+ the Iostatus fields of the request. It will set up any timers
+ that are used to control the read. It will attempt to complete
+ the read from data already in the interrupt buffer. If the
+ read can be completed quickly it will start off another if
+ necessary.
+
+Arguments:
+
+ Extension - Simply a pointer to the serial device extension.
+
+Return Value:
+
+ This routine will return the status of the first read
+ request. This is useful in that if we have a read that can
+ complete right away (AND there had been nothing in the
+ queue before it) the read could return SUCCESS and the
+ application won't have to do a wait.
+
+--*/
+
+{
+
+ SERIAL_UPDATE_CHAR updateChar;
+
+ WDFREQUEST newRequest;
+
+ BOOLEAN returnWithWhatsPresent;
+ BOOLEAN os2ssreturn;
+ BOOLEAN crunchDownToOne;
+ BOOLEAN useTotalTimer;
+ BOOLEAN useIntervalTimer;
+
+ ULONG multiplierVal = 0;
+ ULONG constantVal = 0;
+
+ LARGE_INTEGER totalTime = {0};
+
+ SERIAL_TIMEOUTS timeoutsForIrp;
+
+ PREQUEST_CONTEXT reqContext;
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_READ,
+ ">SerialStartRead(%p)\n", Extension);
+
+ updateChar.Extension = Extension;
+
+
+ do {
+
+ reqContext = SerialGetRequestContext(Extension->CurrentReadRequest);
+
+ //
+ // Check to see if this is a resize request. If it is
+ // then go to a routine that specializes in that.
+ //
+
+ if (reqContext->MajorFunction != IRP_MJ_READ) {
+
+ NTSTATUS localStatus = SerialResizeBuffer(Extension);
+ UNREFERENCED_PARAMETER(localStatus);
+ ASSERT(NT_SUCCESS(localStatus));
+
+ } else {
+
+ Extension->NumberNeededForRead = reqContext->Length;
+
+ //
+ // Calculate the timeout value needed for the
+ // request. Note that the values stored in the
+ // timeout record are in milliseconds.
+ //
+
+ useTotalTimer = FALSE;
+ returnWithWhatsPresent = FALSE;
+ os2ssreturn = FALSE;
+ crunchDownToOne = FALSE;
+ useIntervalTimer = FALSE;
+
+ //
+ //
+ // CIMEXCIMEX -- this is a lie
+ //
+ // Always initialize the timer objects so that the
+ // completion code can tell when it attempts to
+ // cancel the timers whether the timers had ever
+ // been Set.
+ //
+ // CIMEXCIMEX -- this is the truth
+ //
+ // What we want to do is just make sure the timers are
+ // cancelled to the best of our ability and move on with
+ // life.
+ //
+
+ SerialCancelTimer(Extension->ReadRequestTotalTimer, Extension);
+ SerialCancelTimer(Extension->ReadRequestIntervalTimer, Extension);
+
+ //
+ // We get the *current* timeout values to use for timing
+ // this read.
+ //
+
+
+ timeoutsForIrp = Extension->Timeouts;
+
+ //
+ // Calculate the interval timeout for the read.
+ //
+
+ if (timeoutsForIrp.ReadIntervalTimeout &&
+ (timeoutsForIrp.ReadIntervalTimeout !=
+ MAXULONG)) {
+
+ useIntervalTimer = TRUE;
+
+ Extension->IntervalTime.QuadPart =
+ UInt32x32To64(
+ timeoutsForIrp.ReadIntervalTimeout,
+ 10000
+ );
+
+
+ if (Extension->IntervalTime.QuadPart >=
+ Extension->CutOverAmount.QuadPart) {
+
+ Extension->IntervalTimeToUse =
+ &Extension->LongIntervalAmount;
+
+ } else {
+
+ Extension->IntervalTimeToUse =
+ &Extension->ShortIntervalAmount;
+
+ }
+
+ }
+
+ if (timeoutsForIrp.ReadIntervalTimeout == MAXULONG) {
+
+ //
+ // We need to do special return quickly stuff here.
+ //
+ // 1) If both constant and multiplier are
+ // 0 then we return immediately with whatever
+ // we've got, even if it was zero.
+ //
+ // 2) If constant and multiplier are not MAXULONG
+ // then return immediately if any characters
+ // are present, but if nothing is there, then
+ // use the timeouts as specified.
+ //
+ // 3) If multiplier is MAXULONG then do as in
+ // "2" but return when the first character
+ // arrives.
+ //
+
+ if (!timeoutsForIrp.ReadTotalTimeoutConstant &&
+ !timeoutsForIrp.ReadTotalTimeoutMultiplier) {
+
+ returnWithWhatsPresent = TRUE;
+
+ } else if ((timeoutsForIrp.ReadTotalTimeoutConstant != MAXULONG)
+ &&
+ (timeoutsForIrp.ReadTotalTimeoutMultiplier
+ != MAXULONG)) {
+
+ useTotalTimer = TRUE;
+ os2ssreturn = TRUE;
+ multiplierVal = timeoutsForIrp.ReadTotalTimeoutMultiplier;
+ constantVal = timeoutsForIrp.ReadTotalTimeoutConstant;
+
+ } else if ((timeoutsForIrp.ReadTotalTimeoutConstant != MAXULONG)
+ &&
+ (timeoutsForIrp.ReadTotalTimeoutMultiplier
+ == MAXULONG)) {
+
+ useTotalTimer = TRUE;
+ os2ssreturn = TRUE;
+ crunchDownToOne = TRUE;
+ multiplierVal = 0;
+ constantVal = timeoutsForIrp.ReadTotalTimeoutConstant;
+
+ }
+
+ } else {
+
+ //
+ // If both the multiplier and the constant are
+ // zero then don't do any total timeout processing.
+ //
+
+ if (timeoutsForIrp.ReadTotalTimeoutMultiplier ||
+ timeoutsForIrp.ReadTotalTimeoutConstant) {
+
+ //
+ // We have some timer values to calculate.
+ //
+
+ useTotalTimer = TRUE;
+ multiplierVal = timeoutsForIrp.ReadTotalTimeoutMultiplier;
+ constantVal = timeoutsForIrp.ReadTotalTimeoutConstant;
+
+ }
+
+ }
+
+ if (useTotalTimer) {
+
+ totalTime.QuadPart = ((LONGLONG)(UInt32x32To64(
+ Extension->NumberNeededForRead,
+ multiplierVal
+ )
+ + constantVal))
+ * -10000;
+
+ }
+
+
+ //
+ // We do this copy in the hope of getting most (if not
+ // all) of the characters out of the interrupt buffer.
+ //
+ // Note that we need to protect this operation with a
+ // spinlock since we don't want a purge to hose us.
+ //
+
+ updateChar.CharsCopied = SerialGetCharsFromIntBuffer(Extension);
+
+ //
+ // See if we have any cause to return immediately.
+ //
+
+ if (returnWithWhatsPresent || (!Extension->NumberNeededForRead) ||
+ (os2ssreturn &&
+ reqContext->Information)) {
+
+ //
+ // We got all we needed for this read.
+ // Update the number of characters in the
+ // interrupt read buffer.
+ //
+
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SerialUpdateInterruptBuffer,
+ &updateChar
+ );
+
+ reqContext->Status = STATUS_SUCCESS;
+
+ } else {
+
+ //
+ // The request might go under control of the isr. It
+ // won't hurt to initialize the reference count
+ // right now.
+ //
+
+ SERIAL_INIT_REFERENCE(reqContext);
+
+ //
+ // If we are supposed to crunch the read down to
+ // one character, then update the read length
+ // in the request and truncate the number needed for
+ // read down to one. Note that if we are doing
+ // this crunching, then the information must be
+ // zero (or we would have completed above) and
+ // the number needed for the read must still be
+ // equal to the read length.
+ //
+
+ if (crunchDownToOne) {
+
+ ASSERT(
+ (!reqContext->Information)
+ &&
+ (Extension->NumberNeededForRead == reqContext->Length)
+ );
+
+ Extension->NumberNeededForRead = 1;
+ reqContext->Length = 1;
+
+ }
+
+ //
+ // We still need to get more characters for this read.
+ // synchronize with the isr so that we can update the
+ // number of characters and if necessary it will have the
+ // isr switch to copying into the users buffer.
+ //
+
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SerialUpdateAndSwitchToUser,
+ &updateChar
+ );
+
+ if (!updateChar.Completed) {
+
+ SerialSetCancelRoutine(Extension->CurrentReadRequest,
+ SerialCancelCurrentRead);
+
+ //
+ // The request still isn't complete. The
+ // completion routines will end up reinvoking
+ // this routine. So we simply leave.
+ //
+ // First thought we should start off the total
+ // timer for the read and increment the reference
+ // count that the total timer has on the current
+ // request. Note that this is safe, because even if
+ // the io has been satisfied by the isr it can't
+ // complete yet because we still own the cancel
+ // spinlock.
+ //
+
+ if (useTotalTimer) {
+ BOOLEAN result;
+
+ result = SerialSetTimer(
+ Extension->ReadRequestTotalTimer,
+ totalTime
+ );
+
+ if(result == FALSE) {
+ SERIAL_SET_REFERENCE(
+ reqContext,
+ SERIAL_REF_TOTAL_TIMER
+ );
+ }
+
+ }
+
+ if (useIntervalTimer) {
+
+ BOOLEAN result;
+
+ KeQuerySystemTime(
+ &Extension->LastReadTime
+
+ );
+ result = SerialSetTimer(
+ Extension->ReadRequestIntervalTimer,
+ *Extension->IntervalTimeToUse
+ );
+
+ if(result == FALSE) {
+ SERIAL_SET_REFERENCE(
+ reqContext,
+ SERIAL_REF_INT_TIMER
+ );
+ }
+
+ }
+
+ break;
+
+ } else {
+
+ reqContext->Status = STATUS_SUCCESS;
+ }
+
+ }
+
+ }
+
+ //
+ // Well the operation is complete.
+ //
+
+ SerialGetNextRequest(&Extension->CurrentReadRequest,
+ Extension->ReadQueue,
+ &newRequest, TRUE, Extension);
+
+ } while (newRequest);
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_READ, "<SerialStartRead \n");
+
+ return;
+
+}
+
+
+VOID
+SerialCompleteRead(
+ IN WDFDPC Dpc
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is merely used to complete any read that
+ ended up being used by the Isr. It assumes that the
+ status and the information fields of the request are already
+ correctly filled in.
+
+Arguments:
+
+ Dpc - Not Used.
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION extension = NULL;
+
+ extension = SerialGetDeviceExtension(WdfDpcGetParentObject(Dpc));
+
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_READ, ">SerialCompleteRead(%p)\n",
+ extension);
+
+ //
+ // We set this to indicate to the interval timer
+ // that the read has completed.
+ //
+ // Recall that the interval timer dpc can be lurking in some
+ // DPC queue.
+ //
+
+ extension->CountOnLastRead = SERIAL_COMPLETE_READ_COMPLETE;
+
+ SerialTryToCompleteCurrent(
+ extension,
+ NULL,
+ STATUS_SUCCESS,
+ &extension->CurrentReadRequest,
+ extension->ReadQueue,
+ extension->ReadRequestIntervalTimer,
+ extension->ReadRequestTotalTimer,
+ SerialStartRead,
+ SerialGetNextRequest,
+ SERIAL_REF_ISR
+ );
+
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_READ, "<SerialCompleteRead\n");
+}
+
+
+VOID
+SerialCancelCurrentRead(
+ WDFREQUEST Request
+ )
+/*++
+
+Routine Description:
+
+ This routine is used to cancel the current read.
+
+Arguments:
+
+ Device - Wdf device handle
+
+ Request - Pointer to the WDFREQUEST to be canceled.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION extension = NULL;
+ WDFDEVICE device = WdfIoQueueGetDevice(WdfRequestGetIoQueue(Request));
+
+ UNREFERENCED_PARAMETER(Request);
+
+ extension = SerialGetDeviceExtension(device);
+
+ //
+ // We set this to indicate to the interval timer
+ // that the read has encountered a cancel.
+ //
+ // Recall that the interval timer dpc can be lurking in some
+ // DPC queue.
+ //
+
+ extension->CountOnLastRead = SERIAL_COMPLETE_READ_CANCEL;
+
+ SerialTryToCompleteCurrent(
+ extension,
+ SerialGrabReadFromIsr,
+ STATUS_CANCELLED,
+ &extension->CurrentReadRequest,
+ extension->ReadQueue,
+ extension->ReadRequestIntervalTimer,
+ extension->ReadRequestTotalTimer,
+ SerialStartRead,
+ SerialGetNextRequest,
+ SERIAL_REF_CANCEL
+ );
+
+}
+
+
+BOOLEAN
+SerialGrabReadFromIsr(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to grab (if possible) the request from the
+ isr. If it finds that the isr still owns the request it grabs
+ the ipr away (updating the number of characters copied into the
+ users buffer). If it grabs it away it also decrements the
+ reference count on the request since it no longer belongs to the
+ isr (and the dpc that would complete it).
+
+ NOTE: This routine assumes that if the current buffer that the
+ ISR is copying characters into is the interrupt buffer then
+ the dpc has already been queued.
+
+ NOTE: This routine is being called from WdfInterruptSynchronize.
+
+ NOTE: This routine assumes that it is called with the cancel spin
+ lock held.
+
+Arguments:
+
+ Context - Really a pointer to the device extension.
+
+Return Value:
+
+ Always false.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION extension = Context;
+ PREQUEST_CONTEXT reqContext;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ reqContext = SerialGetRequestContext(extension->CurrentReadRequest);
+
+ if (extension->ReadBufferBase !=
+ extension->InterruptReadBuffer) {
+
+ //
+ // We need to set the information to the number of characters
+ // that the read wanted minus the number of characters that
+ // didn't get read into the interrupt buffer.
+ //
+
+ reqContext->Information = reqContext->Length -
+ ((extension->LastCharSlot - extension->CurrentCharSlot) + 1);
+
+ //
+ // Switch back to the interrupt buffer.
+ //
+
+ extension->ReadBufferBase = extension->InterruptReadBuffer;
+ extension->CurrentCharSlot = extension->InterruptReadBuffer;
+ extension->FirstReadableChar = extension->InterruptReadBuffer;
+ extension->LastCharSlot = extension->InterruptReadBuffer +
+ (extension->BufferSize - 1);
+ extension->CharsInInterruptBuffer = 0;
+
+ SERIAL_CLEAR_REFERENCE(
+ reqContext,
+ SERIAL_REF_ISR
+ );
+
+ }
+
+ return FALSE;
+
+}
+
+VOID
+SerialReadTimeout(
+ IN WDFTIMER Timer
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to complete a read because its total
+ timer has expired.
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION extension = NULL;
+
+ extension = SerialGetDeviceExtension(WdfTimerGetParentObject(Timer));
+
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_READ, ">SerialReadTimeout(%p)\n",
+ extension);
+
+ //
+ // We set this to indicate to the interval timer
+ // that the read has completed due to total timeout.
+ //
+ // Recall that the interval timer dpc can be lurking in some
+ // DPC queue.
+ //
+
+ extension->CountOnLastRead = SERIAL_COMPLETE_READ_TOTAL;
+
+ SerialTryToCompleteCurrent(
+ extension,
+ SerialGrabReadFromIsr,
+ STATUS_TIMEOUT,
+ &extension->CurrentReadRequest,
+ extension->ReadQueue,
+ extension->ReadRequestIntervalTimer,
+ extension->ReadRequestTotalTimer,
+ SerialStartRead,
+ SerialGetNextRequest,
+ SERIAL_REF_TOTAL_TIMER
+ );
+
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_READ, "<SerialReadTimeout\n");
+}
+
+
+BOOLEAN
+SerialUpdateReadByIsr(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to update the count of characters read
+ by the isr since the last interval timer experation.
+
+ NOTE: This routine is being called from WdfInterruptSynchronize.
+
+ NOTE: This routine assumes that it is called with the cancel spin
+ lock held.
+
+Arguments:
+
+ Context - Really a pointer to the device extension.
+
+Return Value:
+
+ Always false.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION extension = Context;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ extension->CountOnLastRead = extension->ReadByIsr;
+ extension->ReadByIsr = 0;
+
+ return FALSE;
+
+}
+
+
+VOID
+SerialIntervalReadTimeout(
+ IN WDFTIMER Timer
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used timeout the request if the time between
+ characters exceed the interval time. A global is kept in
+ the device extension that records the count of characters read
+ the last the last time this routine was invoked (This dpc
+ will resubmit the timer if the count has changed). If the
+ count has not changed then this routine will attempt to complete
+ the request. Note the special case of the last count being zero.
+ The timer isn't really in effect until the first character is
+ read.
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PSERIAL_DEVICE_EXTENSION extension = NULL;
+
+ extension = SerialGetDeviceExtension(WdfTimerGetParentObject(Timer));
+
+
+ //SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_READ, ">SerialIntervalReadTimeout(%p)\n",
+ // extension);
+
+ if (extension->CountOnLastRead == SERIAL_COMPLETE_READ_TOTAL) {
+
+ //
+ // This value is only set by the total
+ // timer to indicate that it has fired.
+ // If so, then we should simply try to complete.
+ //
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_INIT, "in SERIAL_COMPLETE_READ_TOTAL\n");
+
+ SerialTryToCompleteCurrent(
+ extension,
+ SerialGrabReadFromIsr,
+ STATUS_TIMEOUT,
+ &extension->CurrentReadRequest,
+ extension->ReadQueue,
+ extension->ReadRequestIntervalTimer,
+ extension->ReadRequestTotalTimer,
+ SerialStartRead,
+ SerialGetNextRequest,
+ SERIAL_REF_INT_TIMER
+ );
+
+ } else if (extension->CountOnLastRead == SERIAL_COMPLETE_READ_COMPLETE) {
+
+ //
+ // This value is only set by the regular
+ // completion routine.
+ //
+ // If so, then we should simply try to complete.
+ //
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_INIT, "in SERIAL_COMPLETE_READ_COMPLETE\n");
+
+ SerialTryToCompleteCurrent(
+ extension,
+ SerialGrabReadFromIsr,
+ STATUS_SUCCESS,
+ &extension->CurrentReadRequest,
+ extension->ReadQueue,
+ extension->ReadRequestIntervalTimer,
+ extension->ReadRequestTotalTimer,
+ SerialStartRead,
+ SerialGetNextRequest,
+ SERIAL_REF_INT_TIMER
+ );
+
+ } else if (extension->CountOnLastRead == SERIAL_COMPLETE_READ_CANCEL) {
+
+ //
+ // This value is only set by the cancel
+ // read routine.
+ //
+ // If so, then we should simply try to complete.
+ //
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_INIT, "in SERIAL_COMPLETE_READ_CANCEL\n");
+
+ SerialTryToCompleteCurrent(
+ extension,
+ SerialGrabReadFromIsr,
+ STATUS_CANCELLED,
+ &extension->CurrentReadRequest,
+ extension->ReadQueue,
+ extension->ReadRequestIntervalTimer,
+ extension->ReadRequestTotalTimer,
+ SerialStartRead,
+ SerialGetNextRequest,
+ SERIAL_REF_INT_TIMER
+ );
+
+ } else if (extension->CountOnLastRead || extension->ReadByIsr) {
+
+ //
+ // Something has happened since we last came here. We
+ // check to see if the ISR has read in any more characters.
+ // If it did then we should update the isr's read count
+ // and resubmit the timer.
+ //
+
+ if (extension->ReadByIsr) {
+
+ WdfInterruptSynchronize(
+ extension->WdfInterrupt,
+ SerialUpdateReadByIsr,
+ extension
+ );
+
+ //
+ // Save off the "last" time something was read.
+ // As we come back to this routine we will compare
+ // the current time to the "last" time. If the
+ // difference is ever larger then the interval
+ // requested by the user, then time out the request.
+ //
+
+ KeQuerySystemTime(
+ &extension->LastReadTime
+ );
+
+ SerialSetTimer(
+ extension->ReadRequestIntervalTimer,
+ *extension->IntervalTimeToUse
+ );
+
+ } else {
+
+ //
+ // Take the difference between the current time
+ // and the last time we had characters and
+ // see if it is greater then the interval time.
+ // if it is, then time out the request. Otherwise
+ // go away again for a while.
+ //
+
+ //
+ // No characters read in the interval time. Kill
+ // this read.
+ //
+
+ LARGE_INTEGER currentTime;
+
+ KeQuerySystemTime(
+ &currentTime
+ );
+
+ if ((currentTime.QuadPart - extension->LastReadTime.QuadPart) >=
+ extension->IntervalTime.QuadPart) {
+
+ SerialTryToCompleteCurrent(
+ extension,
+ SerialGrabReadFromIsr,
+ STATUS_TIMEOUT,
+ &extension->CurrentReadRequest,
+ extension->ReadQueue,
+ extension->ReadRequestIntervalTimer,
+ extension->ReadRequestTotalTimer,
+ SerialStartRead,
+ SerialGetNextRequest,
+ SERIAL_REF_INT_TIMER
+ );
+
+ } else {
+
+ SerialSetTimer(
+ extension->ReadRequestIntervalTimer,
+ *extension->IntervalTimeToUse
+ );
+
+ }
+
+
+ }
+
+ } else {
+
+ //
+ // Timer doesn't really start until the first character.
+ // So we should simply resubmit ourselves.
+ //
+
+ SerialSetTimer(
+ extension->ReadRequestIntervalTimer,
+ *extension->IntervalTimeToUse
+ );
+
+ }
+
+
+ //SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_READ, "<SerialIntervalReadTimeout\n");
+}
+
+
+ULONG
+SerialGetCharsFromIntBuffer(
+ PSERIAL_DEVICE_EXTENSION Extension
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to copy any characters out of the interrupt
+ buffer into the users buffer. It will be reading values that
+ are updated with the ISR but this is safe since this value is
+ only decremented by synchronization routines. This routine will
+ return the number of characters copied so some other routine
+ can call a synchronization routine to update what is seen at
+ interrupt level.
+
+Arguments:
+
+ Extension - A pointer to the device extension.
+
+Return Value:
+
+ The number of characters that were copied into the user
+ buffer.
+
+--*/
+
+{
+
+ //
+ // This value will be the number of characters that this
+ // routine returns. It will be the minimum of the number
+ // of characters currently in the buffer or the number of
+ // characters required for the read.
+ //
+ ULONG numberOfCharsToGet;
+
+ //
+ // This holds the number of characters between the first
+ // readable character and - the last character we will read or
+ // the real physical end of the buffer (not the last readable
+ // character).
+ //
+ ULONG firstTryNumberToGet;
+
+ PREQUEST_CONTEXT reqContext = SerialGetRequestContext(Extension->CurrentReadRequest);
+
+ //
+ // The minimum of the number of characters we need and
+ // the number of characters available
+ //
+
+ numberOfCharsToGet = Extension->CharsInInterruptBuffer;
+
+ if (numberOfCharsToGet > Extension->NumberNeededForRead) {
+
+ numberOfCharsToGet = Extension->NumberNeededForRead;
+
+ }
+
+ if (numberOfCharsToGet) {
+
+ //
+ // This will hold the number of characters between the
+ // first available character and the end of the buffer.
+ // Note that the buffer could wrap around but for the
+ // purposes of the first copy we don't care about that.
+ //
+
+ firstTryNumberToGet = (ULONG)(Extension->LastCharSlot -
+ Extension->FirstReadableChar) + 1;
+
+ if (firstTryNumberToGet > numberOfCharsToGet) {
+
+ //
+ // The characters don't wrap. Actually they may wrap but
+ // we don't care for the purposes of this read since the
+ // characters we need are available before the wrap.
+ //
+
+ RtlMoveMemory(
+ ((PUCHAR)(reqContext->SystemBuffer))
+ + (reqContext->Length - Extension->NumberNeededForRead),
+ Extension->FirstReadableChar,
+ numberOfCharsToGet
+ );
+
+ Extension->NumberNeededForRead -= numberOfCharsToGet;
+
+ //
+ // We now will move the pointer to the first character after
+ // what we just copied into the users buffer.
+ //
+ // We need to check if the stream of readable characters
+ // is wrapping around to the beginning of the buffer.
+ //
+ // Note that we may have just taken the last characters
+ // at the end of the buffer.
+ //
+
+ if ((Extension->FirstReadableChar + (numberOfCharsToGet - 1)) ==
+ Extension->LastCharSlot) {
+
+ Extension->FirstReadableChar = Extension->InterruptReadBuffer;
+
+ } else {
+
+ Extension->FirstReadableChar += numberOfCharsToGet;
+
+ }
+
+ } else {
+
+ //
+ // The characters do wrap. Get up until the end of the buffer.
+ //
+
+ RtlMoveMemory(
+ ((PUCHAR)(reqContext->SystemBuffer))
+ + (reqContext->Length - Extension->NumberNeededForRead),
+ Extension->FirstReadableChar,
+ firstTryNumberToGet
+ );
+
+ Extension->NumberNeededForRead -= firstTryNumberToGet;
+
+ //
+ // Now get the rest of the characters from the beginning of the
+ // buffer.
+ //
+
+ RtlMoveMemory(
+ ((PUCHAR)(reqContext->SystemBuffer))
+ + (reqContext->Length - Extension->NumberNeededForRead),
+ Extension->InterruptReadBuffer,
+ numberOfCharsToGet - firstTryNumberToGet
+ );
+
+ Extension->FirstReadableChar = Extension->InterruptReadBuffer +
+ (numberOfCharsToGet -
+ firstTryNumberToGet);
+
+ Extension->NumberNeededForRead -= (numberOfCharsToGet -
+ firstTryNumberToGet);
+
+ }
+
+ }
+
+ reqContext->Information += numberOfCharsToGet;
+ return numberOfCharsToGet;
+
+}
+
+
+BOOLEAN
+SerialUpdateInterruptBuffer(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to update the number of characters that
+ remain in the interrupt buffer. We need to use this routine
+ since the count could be updated during the update by execution
+ of the ISR.
+
+ NOTE: This is called by WdfInterruptSynchronize.
+
+Arguments:
+
+ Context - Points to a structure that contains a pointer to the
+ device extension and count of the number of characters
+ that we previously copied into the users buffer. The
+ structure actually has a third field that we don't
+ use in this routine.
+
+Return Value:
+
+ Always FALSE.
+
+--*/
+
+{
+
+ PSERIAL_UPDATE_CHAR update = Context;
+ PSERIAL_DEVICE_EXTENSION extension = update->Extension;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ ASSERT(extension->CharsInInterruptBuffer >= update->CharsCopied);
+ extension->CharsInInterruptBuffer -= update->CharsCopied;
+
+ //
+ // Deal with flow control if necessary.
+ //
+
+ SerialHandleReducedIntBuffer(extension);
+
+
+ return FALSE;
+
+}
+
+
+BOOLEAN
+SerialUpdateAndSwitchToUser(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine gets the (hopefully) few characters that
+ remain in the interrupt buffer after the first time we tried
+ to get them out. If we still don't have enough characters
+ to satisfy the read it will then we set things up so that the
+ ISR uses the user buffer copy into.
+
+ This routine is also used to update a count that is maintained
+ by the ISR to keep track of the number of characters in its buffer.
+
+ NOTE: This is called by WdfInterruptSynchronize.
+
+Arguments:
+
+ Context - Points to a structure that contains a pointer to the
+ device extension, a count of the number of characters
+ that we previously copied into the users buffer, and
+ a boolean that we will set that defines whether we
+ switched the ISR to copy into the users buffer.
+
+Return Value:
+
+ Always FALSE.
+
+--*/
+
+{
+
+ PSERIAL_UPDATE_CHAR updateChar = Context;
+ PSERIAL_DEVICE_EXTENSION extension = updateChar->Extension;
+ PREQUEST_CONTEXT reqContext;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ reqContext = SerialGetRequestContext(extension->CurrentReadRequest);
+
+ SerialUpdateInterruptBuffer(extension->WdfInterrupt, Context);
+
+ //
+ // There are more characters to get to satisfy this read.
+ // Copy any characters that have arrived since we got
+ // the last batch.
+ //
+
+ updateChar->CharsCopied = SerialGetCharsFromIntBuffer(extension);
+
+ SerialUpdateInterruptBuffer(extension->WdfInterrupt, Context);
+
+ //
+ // No more new characters will be "received" until we exit
+ // this routine. We again check to make sure that we
+ // haven't satisfied this read, and if we haven't we set things
+ // up so that the ISR copies into the user buffer.
+ //
+
+ if (extension->NumberNeededForRead) {
+
+ //
+ // We shouldn't be switching unless there are no
+ // characters left.
+ //
+
+ ASSERT(!extension->CharsInInterruptBuffer);
+
+ //
+ // We use the following to values to do inteval timing.
+ //
+ // CountOnLastRead is mostly used to simply prevent
+ // the interval timer from timing out before any characters
+ // are read. (Interval timing should only be effective
+ // after the first character is read.)
+ //
+ // After the first time the interval timer fires and
+ // characters have be read we will simply update with
+ // the value of ReadByIsr and then set ReadByIsr to zero.
+ // (We do that in a synchronization routine.
+ //
+ // If the interval timer dpc routine ever encounters
+ // ReadByIsr == 0 when CountOnLastRead is non-zero it
+ // will timeout the read.
+ //
+ // (Note that we have a special case of CountOnLastRead
+ // < 0. This is done by the read completion routines other
+ // than the total timeout dpc to indicate that the total
+ // timeout has expired.)
+ //
+
+ extension->CountOnLastRead = (LONG)reqContext->Information;
+
+ extension->ReadByIsr = 0;
+
+ //
+ // By compareing the read buffer base address to the
+ // the base address of the interrupt buffer the ISR
+ // can determine whether we are using the interrupt
+ // buffer or the user buffer.
+ //
+
+ extension->ReadBufferBase = reqContext->SystemBuffer;
+
+ //
+ // The current char slot is after the last copied in
+ // character. We know there is always room since we
+ // we wouldn't have gotten here if there wasn't.
+ //
+
+ extension->CurrentCharSlot = extension->ReadBufferBase +
+ reqContext->Information;
+
+ //
+ // The last position that a character can go is on the
+ // last byte of user buffer. While the actual allocated
+ // buffer space may be bigger, we know that there is at
+ // least as much as the read length.
+ //
+
+ extension->LastCharSlot = extension->ReadBufferBase +
+ (reqContext->Length - 1);
+#if 0 // We set the cancel before calling this routine in StartRead
+ //
+ // Mark the request as being in a cancelable state.
+ //
+ IoSetCancelRoutine(
+ extension->CurrentReadIrp,
+ SerialCancelCurrentRead
+ );
+
+ SERIAL_SET_REFERENCE(
+ reqContext,
+ SERIAL_REF_CANCEL
+ );
+#endif
+ //
+ // Increment the reference count twice.
+ //
+ // Once for the Isr owning the request and once
+ // because the cancel routine has a reference
+ // to it.
+ //
+
+ SERIAL_SET_REFERENCE(
+ reqContext,
+ SERIAL_REF_ISR
+ );
+
+ updateChar->Completed = FALSE;
+
+ } else {
+
+ updateChar->Completed = TRUE;
+
+ }
+
+ return FALSE;
+
+}
+//
+// We use this structure only to communicate to the synchronization
+// routine when we are switching to the resized buffer.
+//
+typedef struct _SERIAL_RESIZE_PARAMS {
+ PSERIAL_DEVICE_EXTENSION Extension;
+ PUCHAR OldBuffer;
+ PUCHAR NewBuffer;
+ ULONG NewBufferSize;
+ ULONG NumberMoved;
+ } SERIAL_RESIZE_PARAMS,*PSERIAL_RESIZE_PARAMS;
+
+
+NTSTATUS
+SerialResizeBuffer(
+ IN PSERIAL_DEVICE_EXTENSION Extension
+ )
+
+/*++
+
+Routine Description:
+
+ This routine will process the resize buffer request.
+ If size requested for the RX buffer is smaller than
+ the current buffer then we will simply return
+ STATUS_SUCCESS. (We don't want to make buffers smaller.
+ If we did that then we all of a sudden have "overrun"
+ problems to deal with as well as flow control to deal
+ with - very painful.) We ignore the TX buffer size
+ request since we don't use a TX buffer.
+
+Arguments:
+
+ Extension - Pointer to the device extension for the port.
+
+Return Value:
+
+ STATUS_SUCCESS if everything worked out ok.
+ STATUS_INSUFFICIENT_RESOURCES if we couldn't allocate the
+ memory for the buffer.
+
+--*/
+
+{
+
+ PREQUEST_CONTEXT reqContext = SerialGetRequestContext(Extension->CurrentReadRequest);
+ PSERIAL_QUEUE_SIZE rs = reqContext->SystemBuffer;
+
+ PVOID newBuffer = reqContext->Type3InputBuffer;
+
+
+ reqContext->Type3InputBuffer = NULL;
+ reqContext->Information = 0L;
+ reqContext->Status = STATUS_SUCCESS;
+
+ if (rs->InSize <= Extension->BufferSize) {
+
+ //
+ // Nothing to do. We don't make buffers smaller. Just
+ // agree with the user. We must deallocate the memory
+ // that was already allocated in the ioctl dispatch routine.
+ //
+
+ ExFreePool(newBuffer);
+
+ } else {
+
+ SERIAL_RESIZE_PARAMS rp;
+
+ //
+ // Hmmm, looks like we actually have to go
+ // through with this. We need to move all the
+ // data that is in the current buffer into this
+ // new buffer. We'll do this in two steps.
+ //
+ // First we go up to dispatch level and try to
+ // move as much as we can without stopping the
+ // ISR from running. We go up to dispatch level
+ // by acquiring the control lock. We do it at
+ // dispatch using the control lock so that:
+ //
+ // 1) We can't be context switched in the middle
+ // of the move. Our pointers into the buffer
+ // could be *VERY* stale by the time we got back.
+ //
+ // 2) We use the control lock since we don't want
+ // some pesky purge request to come along while
+ // we are trying to move.
+ //
+ // After the move, but while we still hold the control
+ // lock, we synch with the ISR and get those last
+ // (hopefully) few characters that have come in since
+ // we started the copy. We switch all of our pointers,
+ // counters, and such to point to this new buffer. NOTE:
+ // we need to be careful. If the buffer we were using
+ // was not the default one created when we initialized
+ // the device (i.e. it was created via a previous WDFREQUEST of
+ // this type), we should deallocate it.
+ //
+
+ rp.Extension = Extension;
+ rp.OldBuffer = Extension->InterruptReadBuffer;
+ rp.NewBuffer = newBuffer;
+ rp.NewBufferSize = rs->InSize;
+
+ rp.NumberMoved = SerialMoveToNewIntBuffer(
+ Extension,
+ newBuffer
+ );
+
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SerialUpdateAndSwitchToNew,
+ &rp
+ );
+
+ //
+ // Free up the memory that the old buffer consumed.
+ //
+
+ ExFreePool(rp.OldBuffer);
+
+ }
+
+ return STATUS_SUCCESS;
+
+}
+
+
+ULONG
+SerialMoveToNewIntBuffer(
+ PSERIAL_DEVICE_EXTENSION Extension,
+ PUCHAR NewBuffer
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to copy any characters out of the interrupt
+ buffer into the "new" buffer. It will be reading values that
+ are updated with the ISR but this is safe since this value is
+ only decremented by synchronization routines. This routine will
+ return the number of characters copied so some other routine
+ can call a synchronization routine to update what is seen at
+ interrupt level.
+
+Arguments:
+
+ Extension - A pointer to the device extension.
+ NewBuffer - Where the characters are to be move to.
+
+Return Value:
+
+ The number of characters that were copied into the user
+ buffer.
+
+--*/
+
+{
+
+ ULONG numberOfCharsMoved = Extension->CharsInInterruptBuffer;
+
+
+ if (numberOfCharsMoved) {
+
+ //
+ // This holds the number of characters between the first
+ // readable character and the last character we will read or
+ // the real physical end of the buffer (not the last readable
+ // character).
+ //
+ ULONG firstTryNumberToGet = (ULONG)(Extension->LastCharSlot -
+ Extension->FirstReadableChar) + 1;
+
+ if (firstTryNumberToGet >= numberOfCharsMoved) {
+
+ //
+ // The characters don't wrap.
+ //
+
+ RtlMoveMemory(
+ NewBuffer,
+ Extension->FirstReadableChar,
+ numberOfCharsMoved
+ );
+
+ if ((Extension->FirstReadableChar+(numberOfCharsMoved-1)) ==
+ Extension->LastCharSlot) {
+
+ Extension->FirstReadableChar = Extension->InterruptReadBuffer;
+
+ } else {
+
+ Extension->FirstReadableChar += numberOfCharsMoved;
+
+ }
+
+ } else {
+
+ //
+ // The characters do wrap. Get up until the end of the buffer.
+ //
+
+ RtlMoveMemory(
+ NewBuffer,
+ Extension->FirstReadableChar,
+ firstTryNumberToGet
+ );
+
+ //
+ // Now get the rest of the characters from the beginning of the
+ // buffer.
+ //
+
+ RtlMoveMemory(
+ NewBuffer+firstTryNumberToGet,
+ Extension->InterruptReadBuffer,
+ numberOfCharsMoved - firstTryNumberToGet
+ );
+
+ Extension->FirstReadableChar = Extension->InterruptReadBuffer +
+ numberOfCharsMoved - firstTryNumberToGet;
+
+ }
+
+ }
+
+ return numberOfCharsMoved;
+
+}
+
+
+BOOLEAN
+SerialUpdateAndSwitchToNew(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine gets the (hopefully) few characters that
+ remain in the interrupt buffer after the first time we tried
+ to get them out.
+
+ NOTE: This is called by WdfInterruptSynchronize.
+
+Arguments:
+
+ Context - Points to a structure that contains a pointer to the
+ device extension, a pointer to the buffer we are moving
+ to, and a count of the number of characters
+ that we previously copied into the new buffer, and the
+ actual size of the new buffer.
+
+Return Value:
+
+ Always FALSE.
+
+--*/
+
+{
+
+ PSERIAL_RESIZE_PARAMS params = Context;
+ PSERIAL_DEVICE_EXTENSION extension = params->Extension;
+ ULONG tempCharsInInterruptBuffer = extension->CharsInInterruptBuffer;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ ASSERT(extension->CharsInInterruptBuffer >= params->NumberMoved);
+
+ //
+ // We temporarily reduce the chars in interrupt buffer to
+ // "fool" the move routine. We will restore it after the
+ // move.
+ //
+
+ extension->CharsInInterruptBuffer -= params->NumberMoved;
+
+ if (extension->CharsInInterruptBuffer) {
+
+ SerialMoveToNewIntBuffer(
+ extension,
+ params->NewBuffer + params->NumberMoved
+ );
+
+ }
+
+ extension->CharsInInterruptBuffer = tempCharsInInterruptBuffer;
+
+
+ extension->LastCharSlot = params->NewBuffer + (params->NewBufferSize - 1);
+ extension->FirstReadableChar = params->NewBuffer;
+ extension->ReadBufferBase = params->NewBuffer;
+ extension->InterruptReadBuffer = params->NewBuffer;
+ extension->BufferSize = params->NewBufferSize;
+
+ //
+ // We *KNOW* that the new interrupt buffer is larger than the
+ // old buffer. We don't need to worry about it being full.
+ //
+
+ extension->CurrentCharSlot = extension->InterruptReadBuffer +
+ extension->CharsInInterruptBuffer;
+
+ //
+ // We set up the default xon/xoff limits.
+ //
+
+ extension->HandFlow.XoffLimit = extension->BufferSize >> 3;
+ extension->HandFlow.XonLimit = extension->BufferSize >> 1;
+
+ extension->WmiCommData.XoffXmitThreshold = extension->HandFlow.XoffLimit;
+ extension->WmiCommData.XonXmitThreshold = extension->HandFlow.XonLimit;
+
+ extension->BufferSizePt8 = ((3*(extension->BufferSize>>2))+
+ (extension->BufferSize>>4));
+
+ //
+ // Since we (essentially) reduced the percentage of the interrupt
+ // buffer being full, we need to handle any flow control.
+ //
+
+ SerialHandleReducedIntBuffer(extension);
+
+ return FALSE;
+
+}
+
+
diff --git a/serial/serial/registry.c b/serial/serial/registry.c
new file mode 100644
index 00000000..78256a89
--- /dev/null
+++ b/serial/serial/registry.c
@@ -0,0 +1,443 @@
+/*++
+
+Copyright (c) Microsoft Corporation
+
+Module Name:
+
+ registry.c
+
+Abstract:
+
+ This module contains the code that is used to get values from the
+ registry and to manipulate entries in the registry.
+
+Environment:
+
+ Kernel mode
+
+--*/
+
+#include "precomp.h"
+
+#if defined(EVENT_TRACING)
+#include "registry.tmh"
+#endif
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(INIT,SerialGetConfigDefaults)
+#pragma alloc_text(PAGESRP0,SerialGetRegistryKeyValue)
+#pragma alloc_text(PAGESRP0,SerialPutRegistryKeyValue)
+#pragma alloc_text(PAGESRP0,SerialGetFdoRegistryKeyValue)
+#endif // ALLOC_PRAGMA
+
+
+#define PARAMATER_NAME_LEN 80
+
+
+NTSTATUS
+SerialGetConfigDefaults(
+ IN PSERIAL_FIRMWARE_DATA DriverDefaultsPtr,
+ IN WDFDRIVER Driver
+ )
+
+/*++
+
+Routine Description:
+
+ This routine reads the default configuration data from the
+ registry for the serial driver.
+
+ It also builds fields in the registry for several configuration
+ options if they don't exist.
+
+Arguments:
+
+ DriverDefaultsPtr - Pointer to a structure that will contain
+ the default configuration values.
+
+ RegistryPath - points to the entry for this driver in the
+ current control set of the registry.
+
+Return Value:
+
+ STATUS_SUCCESS if we got the defaults, otherwise we failed.
+ The only way to fail this call is if the STATUS_INSUFFICIENT_RESOURCES.
+
+--*/
+
+{
+
+ NTSTATUS status = STATUS_SUCCESS; // return value
+ WDFKEY hKey;
+ DECLARE_UNICODE_STRING_SIZE(valueName,PARAMATER_NAME_LEN);
+
+ status = WdfDriverOpenParametersRegistryKey(Driver,
+ STANDARD_RIGHTS_ALL,
+ WDF_NO_OBJECT_ATTRIBUTES,
+ &hKey);
+ if (!NT_SUCCESS (status)) {
+ return status;
+ }
+
+ status = RtlUnicodeStringPrintf(&valueName,L"BreakOnEntry");
+ if (!NT_SUCCESS (status)) {
+ goto End;
+
+ }
+
+ status = WdfRegistryQueryULong (hKey,
+ &valueName,
+ &DriverDefaultsPtr->ShouldBreakOnEntry);
+
+ if (!NT_SUCCESS (status)) {
+ DriverDefaultsPtr->ShouldBreakOnEntry = 0;
+ }
+
+ status = RtlUnicodeStringPrintf(&valueName,L"DebugLevel");
+ if (!NT_SUCCESS (status)) {
+ goto End;
+ }
+
+ status = WdfRegistryQueryULong (hKey,
+ &valueName,
+ &DriverDefaultsPtr->DebugLevel);
+
+ if (!NT_SUCCESS (status)) {
+ DriverDefaultsPtr->DebugLevel = 0;
+ }
+
+
+ status = RtlUnicodeStringPrintf(&valueName,L"ForceFifoEnable");
+ if (!NT_SUCCESS (status)) {
+ goto End;
+ }
+
+ status = WdfRegistryQueryULong (hKey,
+ &valueName,
+ &DriverDefaultsPtr->ForceFifoEnableDefault);
+
+ if (!NT_SUCCESS (status)) {
+
+ //
+ // If it isn't then write out values so that it could
+ // be adjusted later.
+ //
+ DriverDefaultsPtr->ForceFifoEnableDefault = SERIAL_FORCE_FIFO_DEFAULT;
+
+ status = WdfRegistryAssignULong(hKey,
+ &valueName,
+ DriverDefaultsPtr->ForceFifoEnableDefault
+ );
+ if (!NT_SUCCESS (status)) {
+ goto End;
+ }
+
+ }
+
+ status = RtlUnicodeStringPrintf(&valueName,L"RxFIFO");
+ if (!NT_SUCCESS (status)) {
+ goto End;
+ }
+
+ status = WdfRegistryQueryULong (hKey,
+ &valueName,
+ &DriverDefaultsPtr->RxFIFODefault);
+
+ if (!NT_SUCCESS (status)) {
+
+ DriverDefaultsPtr->RxFIFODefault = SERIAL_RX_FIFO_DEFAULT;
+
+ status = WdfRegistryAssignULong(hKey,
+ &valueName,
+ DriverDefaultsPtr->RxFIFODefault
+ );
+ if (!NT_SUCCESS (status)) {
+ goto End;
+ }
+
+ }
+
+ status = RtlUnicodeStringPrintf(&valueName,L"TxFIFO");
+ if (!NT_SUCCESS (status)) {
+ goto End;
+ }
+
+ status = WdfRegistryQueryULong (hKey,
+ &valueName,
+ &DriverDefaultsPtr->TxFIFODefault);
+
+ if (!NT_SUCCESS (status)) {
+
+ DriverDefaultsPtr->TxFIFODefault = SERIAL_TX_FIFO_DEFAULT;
+
+ status = WdfRegistryAssignULong(hKey,
+ &valueName,
+ DriverDefaultsPtr->TxFIFODefault
+ );
+ if (!NT_SUCCESS (status)) {
+ goto End;
+ }
+
+ }
+
+ status = RtlUnicodeStringPrintf(&valueName,L"PermitShare");
+ if (!NT_SUCCESS (status)) {
+ goto End;
+ }
+
+ status = WdfRegistryQueryULong (hKey,
+ &valueName,
+ &DriverDefaultsPtr->PermitShareDefault);
+
+ if (!NT_SUCCESS (status)) {
+
+ DriverDefaultsPtr->PermitShareDefault = SERIAL_PERMIT_SHARE_DEFAULT;
+
+ status = WdfRegistryAssignULong(hKey,
+ &valueName,
+ DriverDefaultsPtr->PermitShareDefault
+ );
+ if (!NT_SUCCESS (status)) {
+ goto End;
+ }
+
+ }
+
+ status = RtlUnicodeStringPrintf(&valueName,L"LogFifo");
+ if (!NT_SUCCESS (status)) {
+ goto End;
+ }
+
+ status = WdfRegistryQueryULong (hKey,
+ &valueName,
+ &DriverDefaultsPtr->LogFifoDefault);
+
+ if (!NT_SUCCESS (status)) {
+
+ DriverDefaultsPtr->LogFifoDefault = SERIAL_LOG_FIFO_DEFAULT;
+
+ status = WdfRegistryAssignULong(hKey,
+ &valueName,
+ DriverDefaultsPtr->LogFifoDefault
+ );
+ if (!NT_SUCCESS (status)) {
+ goto End;
+ }
+
+ DriverDefaultsPtr->LogFifoDefault = 1;
+ }
+
+
+ status = RtlUnicodeStringPrintf(&valueName,L"UartRemovalDetect");
+ if (!NT_SUCCESS (status)) {
+ goto End;
+ }
+
+ status = WdfRegistryQueryULong (hKey,
+ &valueName,
+ &DriverDefaultsPtr->UartRemovalDetect);
+
+ if (!NT_SUCCESS (status)) {
+ DriverDefaultsPtr->UartRemovalDetect = 0;
+ }
+
+
+End:
+ WdfRegistryClose(hKey);
+ return (status);
+}
+
+BOOLEAN
+SerialGetRegistryKeyValue(
+ IN WDFDEVICE WdfDevice,
+ _In_ PCWSTR Name,
+ OUT PULONG Value
+ )
+/*++
+
+Routine Description:
+
+ Can be used to read any REG_DWORD registry value stored
+ under Device Parameter.
+
+Arguments:
+
+ FdoData - pointer to the device extension
+ Name - Name of the registry value
+ Value -
+
+
+Return Value:
+
+ TRUE if successful
+ FALSE if not present/error in reading registry
+
+--*/
+{
+ WDFKEY hKey = NULL;
+ NTSTATUS status;
+ BOOLEAN retValue = FALSE;
+ UNICODE_STRING valueName;
+
+ PAGED_CODE();
+
+ SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_PNP, ">SerialGetRegistryKeyValue(XXX)\n");
+
+ *Value = 0;
+
+ status = WdfDeviceOpenRegistryKey(WdfDevice,
+ PLUGPLAY_REGKEY_DEVICE,
+ STANDARD_RIGHTS_ALL,
+ WDF_NO_OBJECT_ATTRIBUTES,
+ &hKey);
+
+ if (NT_SUCCESS (status)) {
+
+ RtlInitUnicodeString(&valueName,Name);
+
+ status = WdfRegistryQueryULong (hKey,
+ &valueName,
+ Value);
+
+ if (NT_SUCCESS (status)) {
+ retValue = TRUE;
+ }
+
+ WdfRegistryClose(hKey);
+ }
+
+ SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_PNP, "<--SerialGetRegistryKeyValue %ws %d \n",
+ Name, *Value);
+
+ return retValue;
+}
+
+#define PARAMATER_NAME_LEN 80
+
+BOOLEAN
+SerialPutRegistryKeyValue(
+ IN WDFDEVICE WdfDevice,
+ _In_ PCWSTR Name,
+ IN ULONG Value
+ )
+/*++
+
+Routine Description:
+
+ Can be used to write any REG_DWORD registry value stored
+ under Device Parameter.
+
+Arguments:
+
+
+Return Value:
+
+ TRUE - if write is successful
+ FALSE - otherwise
+
+--*/
+{
+ WDFKEY hKey = NULL;
+ NTSTATUS status;
+ BOOLEAN retValue = FALSE;
+ UNICODE_STRING valueName;
+
+ PAGED_CODE();
+
+ SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_PNP, "Entered PciDrvWriteRegistryValue\n");
+
+ //
+ // write the value out to the registry
+ //
+ status = WdfDeviceOpenRegistryKey(WdfDevice,
+ PLUGPLAY_REGKEY_DEVICE,
+ STANDARD_RIGHTS_ALL,
+ WDF_NO_OBJECT_ATTRIBUTES,
+ &hKey);
+
+ if (NT_SUCCESS (status)) {
+
+ RtlInitUnicodeString(&valueName,Name);
+
+ status = WdfRegistryAssignULong (hKey,
+ &valueName,
+ Value
+ );
+
+ if (NT_SUCCESS (status)) {
+ retValue = TRUE;
+ }
+
+ WdfRegistryClose(hKey);
+ }
+
+ return retValue;
+
+}
+
+BOOLEAN
+SerialGetFdoRegistryKeyValue(
+ IN PWDFDEVICE_INIT DeviceInit,
+ _In_ PCWSTR Name,
+ OUT PULONG Value
+ )
+/*++
+
+Routine Description:
+
+ Can be used to read any REG_DWORD registry value stored
+ under Device Parameter.
+
+Arguments:
+
+ FdoData - pointer to the device extension
+ Name - Name of the registry value
+ Value -
+
+
+Return Value:
+
+ TRUE if successful
+ FALSE if not present/error in reading registry
+
+--*/
+{
+ WDFKEY hKey = NULL;
+ NTSTATUS status;
+ BOOLEAN retValue = FALSE;
+ UNICODE_STRING valueName;
+
+ PAGED_CODE();
+
+ SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_PNP,
+ "-->SerialGetFdoRegistryKeyValue\n");
+
+ *Value = 0;
+
+ status = WdfFdoInitOpenRegistryKey(DeviceInit,
+ PLUGPLAY_REGKEY_DEVICE,
+ STANDARD_RIGHTS_ALL,
+ WDF_NO_OBJECT_ATTRIBUTES,
+ &hKey);
+
+ if (NT_SUCCESS (status)) {
+
+ RtlInitUnicodeString(&valueName,Name);
+
+ status = WdfRegistryQueryULong (hKey, &valueName, Value);
+
+ if (NT_SUCCESS (status)) {
+ retValue = TRUE;
+ }
+
+ WdfRegistryClose(hKey);
+ }
+
+ SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_PNP,
+ "<--SerialGetFdoRegistryKeyValue %ws %d \n",
+ Name, *Value);
+
+ return retValue;
+}
+
+
diff --git a/serial/serial/serial.h b/serial/serial/serial.h
new file mode 100644
index 00000000..7bbe2544
--- /dev/null
+++ b/serial/serial/serial.h
@@ -0,0 +1,1757 @@
+/*++
+
+Copyright (c) 1990, 1991, 1992, 1993 - 1997 Microsoft Corporation
+
+Module Name :
+
+ serial.h
+
+Abstract:
+
+ Type definitions and data for the serial port driver
+
+--*/
+
+#define POOL_TAG 'XMOC'
+
+
+//
+// Some default driver values. We will check the registry for
+// them first.
+//
+#define SERIAL_UNINITIALIZED_DEFAULT 1234567
+#define SERIAL_FORCE_FIFO_DEFAULT 1
+#define SERIAL_RX_FIFO_DEFAULT 8
+#define SERIAL_TX_FIFO_DEFAULT 14
+#define SERIAL_PERMIT_SHARE_DEFAULT 0
+#define SERIAL_LOG_FIFO_DEFAULT 0
+
+
+//
+// This define gives the default Object directory
+// that we should use to insert the symbolic links
+// between the NT device name and namespace used by
+// that object directory.
+#define DEFAULT_DIRECTORY L"DosDevices"
+
+//
+// For the above directory, the serial port will
+// use the following name as the suffix of the serial
+// ports for that directory. It will also append
+// a number onto the end of the name. That number
+// will start at 1.
+#define DEFAULT_SERIAL_NAME L"COM"
+//
+//
+// This define gives the default NT name for
+// for serial ports detected by the firmware.
+// This name will be appended to Device prefix
+// with a number following it. The number is
+// incremented each time encounter a serial
+// port detected by the firmware. Note that
+// on a system with multiple busses, this means
+// that the first port on a bus is not necessarily
+// \Device\Serial0.
+//
+#define DEFAULT_NT_SUFFIX L"Serial"
+#define _DRIVER_NAME_ "Serial.sys"
+
+#define DEVICE_OBJECT_NAME_LENGTH 128
+#define SYMBOLIC_NAME_LENGTH 128
+#define SERIAL_DEVICE_MAP L"SERIALCOMM"
+
+//
+// GUID_DEVINTERFACE_COMPORT is not defined in the Win2K
+// headers, so we will need this definition to avoid compilation
+// errors.
+//
+#define GUID_DEVINTERFACE_COMPORT GUID_CLASS_COMPORT
+
+//
+// This value - which could be redefined at compile
+// time, define the stride between registers
+//
+#if !defined(SERIAL_REGISTER_STRIDE)
+#define SERIAL_REGISTER_STRIDE 1
+#endif
+
+//
+// Offsets from the base register address of the
+// various registers for the 8250 family of UARTS.
+//
+#define RECEIVE_BUFFER_REGISTER ((ULONG)((0x00)*SERIAL_REGISTER_STRIDE))
+#define TRANSMIT_HOLDING_REGISTER ((ULONG)((0x00)*SERIAL_REGISTER_STRIDE))
+#define INTERRUPT_ENABLE_REGISTER ((ULONG)((0x01)*SERIAL_REGISTER_STRIDE))
+#define INTERRUPT_IDENT_REGISTER ((ULONG)((0x02)*SERIAL_REGISTER_STRIDE))
+#define FIFO_CONTROL_REGISTER ((ULONG)((0x02)*SERIAL_REGISTER_STRIDE))
+#define LINE_CONTROL_REGISTER ((ULONG)((0x03)*SERIAL_REGISTER_STRIDE))
+#define MODEM_CONTROL_REGISTER ((ULONG)((0x04)*SERIAL_REGISTER_STRIDE))
+#define LINE_STATUS_REGISTER ((ULONG)((0x05)*SERIAL_REGISTER_STRIDE))
+#define MODEM_STATUS_REGISTER ((ULONG)((0x06)*SERIAL_REGISTER_STRIDE))
+#define DIVISOR_LATCH_LSB ((ULONG)((0x00)*SERIAL_REGISTER_STRIDE))
+#define DIVISOR_LATCH_MSB ((ULONG)((0x01)*SERIAL_REGISTER_STRIDE))
+#define SERIAL_REGISTER_SPAN ((ULONG)(7*SERIAL_REGISTER_STRIDE))
+
+//
+// If we have an interrupt status register this is its assumed
+// length.
+//
+#define SERIAL_STATUS_LENGTH ((ULONG)(1*SERIAL_REGISTER_STRIDE))
+
+//
+// Bitmask definitions for accessing the 8250 device registers.
+//
+
+//
+// These bits define the number of data bits trasmitted in
+// the Serial Data Unit (SDU - Start,data, parity, and stop bits)
+//
+#define SERIAL_DATA_LENGTH_5 0x00
+#define SERIAL_DATA_LENGTH_6 0x01
+#define SERIAL_DATA_LENGTH_7 0x02
+#define SERIAL_DATA_LENGTH_8 0x03
+
+
+//
+// These masks define the interrupts that can be enabled or disabled.
+//
+//
+// This interrupt is used to notify that there is new incomming
+// data available. The SERIAL_RDA interrupt is enabled by this bit.
+//
+#define SERIAL_IER_RDA 0x01
+
+//
+// This interrupt is used to notify that there is space available
+// in the transmitter for another character. The SERIAL_THR
+// interrupt is enabled by this bit.
+//
+#define SERIAL_IER_THR 0x02
+
+//
+// This interrupt is used to notify that some sort of error occured
+// with the incomming data. The SERIAL_RLS interrupt is enabled by
+// this bit.
+#define SERIAL_IER_RLS 0x04
+
+//
+// This interrupt is used to notify that some sort of change has
+// taken place in the modem control line. The SERIAL_MS interrupt is
+// enabled by this bit.
+//
+#define SERIAL_IER_MS 0x08
+
+
+//
+// These masks define the values of the interrupt identification
+// register. The low bit must be clear in the interrupt identification
+// register for any of these interrupts to be valid. The interrupts
+// are defined in priority order, with the highest value being most
+// important. See above for a description of what each interrupt
+// implies.
+//
+#define SERIAL_IIR_RLS 0x06
+#define SERIAL_IIR_RDA 0x04
+#define SERIAL_IIR_CTI 0x0c
+#define SERIAL_IIR_THR 0x02
+#define SERIAL_IIR_MS 0x00
+
+//
+// This bit mask get the value of the high two bits of the
+// interrupt id register. If this is a 16550 class chip
+// these bits will be a one if the fifo's are enbled, otherwise
+// they will always be zero.
+//
+#define SERIAL_IIR_FIFOS_ENABLED 0xc0
+
+//
+// If the low bit is logic one in the interrupt identification register
+// this implies that *NO* interrupts are pending on the device.
+//
+#define SERIAL_IIR_NO_INTERRUPT_PENDING 0x01
+
+
+//
+// Use these bits to detect removal of serial card for Stratus implementation
+//
+#define SERIAL_IIR_MUST_BE_ZERO 0x30
+
+
+//
+// These masks define access to the fifo control register.
+//
+
+//
+// Enabling this bit in the fifo control register will turn
+// on the fifos. If the fifos are enabled then the high two
+// bits of the interrupt id register will be set to one. Note
+// that this only occurs on a 16550 class chip. If the high
+// two bits in the interrupt id register are not one then
+// we know we have a lower model chip.
+//
+//
+#define SERIAL_FCR_ENABLE ((UCHAR)0x01)
+#define SERIAL_FCR_RCVR_RESET ((UCHAR)0x02)
+#define SERIAL_FCR_TXMT_RESET ((UCHAR)0x04)
+
+//
+// This set of values define the high water marks (when the
+// interrupts trip) for the receive fifo.
+//
+#define SERIAL_1_BYTE_HIGH_WATER ((UCHAR)0x00)
+#define SERIAL_4_BYTE_HIGH_WATER ((UCHAR)0x40)
+#define SERIAL_8_BYTE_HIGH_WATER ((UCHAR)0x80)
+#define SERIAL_14_BYTE_HIGH_WATER ((UCHAR)0xc0)
+
+//
+// These masks define access to the line control register.
+//
+
+//
+// This defines the bit used to control the definition of the "first"
+// two registers for the 8250. These registers are the input/output
+// register and the interrupt enable register. When the DLAB bit is
+// enabled these registers become the least significant and most
+// significant bytes of the divisor value.
+//
+#define SERIAL_LCR_DLAB 0x80
+
+//
+// This defines the bit used to control whether the device is sending
+// a break. When this bit is set the device is sending a space (logic 0).
+//
+// Most protocols will assume that this is a hangup.
+//
+#define SERIAL_LCR_BREAK 0x40
+
+//
+// These defines are used to set the line control register.
+//
+#define SERIAL_5_DATA ((UCHAR)0x00)
+#define SERIAL_6_DATA ((UCHAR)0x01)
+#define SERIAL_7_DATA ((UCHAR)0x02)
+#define SERIAL_8_DATA ((UCHAR)0x03)
+#define SERIAL_DATA_MASK ((UCHAR)0x03)
+
+#define SERIAL_1_STOP ((UCHAR)0x00)
+#define SERIAL_1_5_STOP ((UCHAR)0x04) // Only valid for 5 data bits
+#define SERIAL_2_STOP ((UCHAR)0x04) // Not valid for 5 data bits
+#define SERIAL_STOP_MASK ((UCHAR)0x04)
+
+#define SERIAL_NONE_PARITY ((UCHAR)0x00)
+#define SERIAL_ODD_PARITY ((UCHAR)0x08)
+#define SERIAL_EVEN_PARITY ((UCHAR)0x18)
+#define SERIAL_MARK_PARITY ((UCHAR)0x28)
+#define SERIAL_SPACE_PARITY ((UCHAR)0x38)
+#define SERIAL_PARITY_MASK ((UCHAR)0x38)
+
+//
+// These masks define access the modem control register.
+//
+
+//
+// This bit controls the data terminal ready (DTR) line. When
+// this bit is set the line goes to logic 0 (which is then inverted
+// by normal hardware). This is normally used to indicate that
+// the device is available to be used. Some odd hardware
+// protocols (like the kernel debugger) use this for handshaking
+// purposes.
+//
+#define SERIAL_MCR_DTR 0x01
+
+//
+// This bit controls the ready to send (RTS) line. When this bit
+// is set the line goes to logic 0 (which is then inverted by the normal
+// hardware). This is used for hardware handshaking. It indicates that
+// the hardware is ready to send data and it is waiting for the
+// receiving end to set clear to send (CTS).
+//
+#define SERIAL_MCR_RTS 0x02
+
+//
+// This bit is used for general purpose output.
+//
+#define SERIAL_MCR_OUT1 0x04
+
+//
+// This bit is used for general purpose output.
+//
+#define SERIAL_MCR_OUT2 0x08
+
+//
+// This bit controls the loopback testing mode of the device. Basically
+// the outputs are connected to the inputs (and vice versa).
+//
+#define SERIAL_MCR_LOOP 0x10
+
+//
+// This bit enables auto flow control on a TI TL16C550C/TL16C550CI
+//
+
+#define SERIAL_MCR_TL16C550CAFE 0x20
+
+
+//
+// These masks define access to the line status register. The line
+// status register contains information about the status of data
+// transfer. The first five bits deal with receive data and the
+// last two bits deal with transmission. An interrupt is generated
+// whenever bits 1 through 4 in this register are set.
+//
+
+//
+// This bit is the data ready indicator. It is set to indicate that
+// a complete character has been received. This bit is cleared whenever
+// the receive buffer register has been read.
+//
+#define SERIAL_LSR_DR 0x01
+
+//
+// This is the overrun indicator. It is set to indicate that the receive
+// buffer register was not read befor a new character was transferred
+// into the buffer. This bit is cleared when this register is read.
+//
+#define SERIAL_LSR_OE 0x02
+
+//
+// This is the parity error indicator. It is set whenever the hardware
+// detects that the incoming serial data unit does not have the correct
+// parity as defined by the parity select in the line control register.
+// This bit is cleared by reading this register.
+//
+#define SERIAL_LSR_PE 0x04
+
+//
+// This is the framing error indicator. It is set whenever the hardware
+// detects that the incoming serial data unit does not have a valid
+// stop bit. This bit is cleared by reading this register.
+//
+#define SERIAL_LSR_FE 0x08
+
+//
+// This is the break interrupt indicator. It is set whenever the data
+// line is held to logic 0 for more than the amount of time it takes
+// to send one serial data unit. This bit is cleared whenever the
+// this register is read.
+//
+#define SERIAL_LSR_BI 0x10
+
+//
+// This is the transmit holding register empty indicator. It is set
+// to indicate that the hardware is ready to accept another character
+// for transmission. This bit is cleared whenever a character is
+// written to the transmit holding register.
+//
+#define SERIAL_LSR_THRE 0x20
+
+//
+// This bit is the transmitter empty indicator. It is set whenever the
+// transmit holding buffer is empty and the transmit shift register
+// (a non-software accessable register that is used to actually put
+// the data out on the wire) is empty. Basically this means that all
+// data has been sent. It is cleared whenever the transmit holding or
+// the shift registers contain data.
+//
+#define SERIAL_LSR_TEMT 0x40
+
+//
+// This bit indicates that there is at least one error in the fifo.
+// The bit will not be turned off until there are no more errors
+// in the fifo.
+//
+#define SERIAL_LSR_FIFOERR 0x80
+
+
+//
+// These masks are used to access the modem status register.
+// Whenever one of the first four bits in the modem status
+// register changes state a modem status interrupt is generated.
+//
+
+//
+// This bit is the delta clear to send. It is used to indicate
+// that the clear to send bit (in this register) has *changed*
+// since this register was last read by the CPU.
+//
+#define SERIAL_MSR_DCTS 0x01
+
+//
+// This bit is the delta data set ready. It is used to indicate
+// that the data set ready bit (in this register) has *changed*
+// since this register was last read by the CPU.
+//
+#define SERIAL_MSR_DDSR 0x02
+
+//
+// This is the trailing edge ring indicator. It is used to indicate
+// that the ring indicator input has changed from a low to high state.
+//
+#define SERIAL_MSR_TERI 0x04
+
+//
+// This bit is the delta data carrier detect. It is used to indicate
+// that the data carrier bit (in this register) has *changed*
+// since this register was last read by the CPU.
+//
+#define SERIAL_MSR_DDCD 0x08
+
+//
+// This bit contains the (complemented) state of the clear to send
+// (CTS) line.
+//
+#define SERIAL_MSR_CTS 0x10
+
+//
+// This bit contains the (complemented) state of the data set ready
+// (DSR) line.
+//
+#define SERIAL_MSR_DSR 0x20
+
+//
+// This bit contains the (complemented) state of the ring indicator
+// (RI) line.
+//
+#define SERIAL_MSR_RI 0x40
+
+//
+// This bit contains the (complemented) state of the data carrier detect
+// (DCD) line.
+//
+#define SERIAL_MSR_DCD 0x80
+
+//
+// This should be more than enough space to hold then
+// numeric suffix of the device name.
+//
+#define DEVICE_NAME_DELTA 20
+
+
+//
+// Up to 16 Ports Per card. However for sixteen
+// port cards the interrupt status register must me
+// the indexing kind rather then the bitmask kind.
+//
+//
+#define SERIAL_MAX_PORTS_INDEXED (16)
+#define SERIAL_MAX_PORTS_NONINDEXED (8)
+
+typedef struct _CONFIG_DATA {
+ PHYSICAL_ADDRESS Controller;
+ PHYSICAL_ADDRESS TrController;
+ ULONG SpanOfController;
+ ULONG ClockRate;
+ ULONG AddressSpace;
+ ULONG DisablePort;
+ ULONG ForceFifoEnable;
+ ULONG RxFIFO;
+ ULONG TxFIFO;
+ ULONG PermitShare;
+ ULONG PermitSystemWideShare;
+ ULONG LogFifo;
+ KINTERRUPT_MODE InterruptMode;
+ ULONG TrVector;
+ ULONG TrIrql;
+ KAFFINITY Affinity;
+ ULONG TL16C550CAFC;
+ } CONFIG_DATA,*PCONFIG_DATA;
+
+
+//
+// This structure contains configuration data, much of which
+// is read from the registry.
+//
+typedef struct _SERIAL_FIRMWARE_DATA {
+ PDRIVER_OBJECT DriverObject;
+ ULONG ControllersFound;
+ ULONG ForceFifoEnableDefault;
+ ULONG DebugLevel;
+ ULONG ShouldBreakOnEntry;
+ ULONG RxFIFODefault;
+ ULONG TxFIFODefault;
+ ULONG PermitShareDefault;
+ ULONG PermitSystemWideShare;
+ ULONG LogFifoDefault;
+ ULONG UartRemovalDetect;
+ UNICODE_STRING Directory;
+ UNICODE_STRING NtNameSuffix;
+ UNICODE_STRING DirectorySymbolicName;
+ LIST_ENTRY ConfigList;
+} SERIAL_FIRMWARE_DATA,*PSERIAL_FIRMWARE_DATA;
+
+//
+// Default xon/xoff characters.
+//
+#define SERIAL_DEF_XON 0x11
+#define SERIAL_DEF_XOFF 0x13
+
+//
+// Reasons that recption may be held up.
+//
+#define SERIAL_RX_DTR ((ULONG)0x01)
+#define SERIAL_RX_XOFF ((ULONG)0x02)
+#define SERIAL_RX_RTS ((ULONG)0x04)
+#define SERIAL_RX_DSR ((ULONG)0x08)
+
+//
+// Reasons that transmission may be held up.
+//
+#define SERIAL_TX_CTS ((ULONG)0x01)
+#define SERIAL_TX_DSR ((ULONG)0x02)
+#define SERIAL_TX_DCD ((ULONG)0x04)
+#define SERIAL_TX_XOFF ((ULONG)0x08)
+#define SERIAL_TX_BREAK ((ULONG)0x10)
+
+//
+// These values are used by the routines that can be used
+// to complete a read (other than interval timeout) to indicate
+// to the interval timeout that it should complete.
+//
+#define SERIAL_COMPLETE_READ_CANCEL ((LONG)-1)
+#define SERIAL_COMPLETE_READ_TOTAL ((LONG)-2)
+#define SERIAL_COMPLETE_READ_COMPLETE ((LONG)-3)
+
+//
+// These are default values that shouldn't appear in the registry
+//
+#define SERIAL_BAD_VALUE ((ULONG)-1)
+
+
+typedef struct _SERIAL_DEVICE_STATE {
+ //
+ // TRUE if we need to set the state to open
+ // on a powerup
+ //
+
+ BOOLEAN Reopen;
+
+ //
+ // Hardware registers
+ //
+
+ UCHAR IER;
+ // FCR is known by other values
+ UCHAR LCR;
+ UCHAR MCR;
+ // LSR is never written
+ // MSR is never written
+ // SCR is either scratch or interrupt status
+
+
+} SERIAL_DEVICE_STATE, *PSERIAL_DEVICE_STATE;
+
+
+typedef
+UCHAR
+(*PREAD_PORT_UCHAR)(
+ IN UCHAR *Register
+ );
+
+typedef
+VOID
+(*PWRITE_PORT_UCHAR)(
+ IN UCHAR *Register,
+ IN UCHAR Value
+ );
+
+typedef struct _SERIAL_DEVICE_EXTENSION {
+ //
+ // WDF device handle
+ //
+ WDFDEVICE WdfDevice;
+ //
+ // Points to the device object that contains
+ // this device extension.
+ //
+ PDEVICE_OBJECT DeviceObject;
+ //
+ // We keep a pointer around to our device name for dumps
+ // and for creating "external" symbolic links to this
+ // device.
+ //
+ UNICODE_STRING DeviceName;
+ //
+ // Pointer to the driver object
+ //
+
+ PDRIVER_OBJECT DriverObject;
+
+ //
+ // Records whether we actually created the symbolic link name
+ // at driver load time. If we didn't create it, we won't try
+ // to destroy it when we unload.
+ //
+ BOOLEAN CreatedSymbolicLink;
+
+ //
+ // Records whether we actually created an entry in SERIALCOMM
+ // at driver load time. If we didn't create it, we won't try
+ // to destroy it when the device is removed.
+ //
+ BOOLEAN CreatedSerialCommEntry;
+
+ //
+ // Did we update system count for serial ports
+ //
+ BOOLEAN IsSystemConfigInfoUpdated;
+
+ //
+ // Should we expose external interfaces?
+ //
+ ULONG SkipNaming;
+
+ //
+ // Support the TI TL16C550C and TL16C550CI auto flow control
+ //
+
+ ULONG TL16C550CAFC;
+
+ //
+ // Detect removed hardware in intterrupt routine flag
+ //
+ ULONG UartRemovalDetect;
+
+ //
+ // We keep track of whether the somebody has the device currently
+ // opened with a simple boolean. We need to know this so that
+ // spurious interrupts from the device (especially during initialization)
+ // will be ignored. This value is only accessed in the ISR and
+ // is only set via synchronization routines. We may be able
+ // to get rid of this boolean when the code is more fleshed out.
+ //
+ BOOLEAN DeviceIsOpened;
+
+ //
+ // Current state during powerdown
+ //
+
+ SERIAL_DEVICE_STATE DeviceState;
+
+ //
+ // TRUE if we own power policy
+ //
+
+ BOOLEAN OwnsPowerPolicy;
+
+ //
+ // TRUE if we should retain power on close and not aggressively
+ // reduce power consumption
+ //
+
+ BOOLEAN RetainPowerOnClose;
+
+ //
+ // Should we enable wakeup
+ //
+
+ BOOLEAN IsWakeEnabled;
+
+ //
+ // This list head is used to contain the time ordered list
+ // of read requests. Access to this list is protected by
+ // the global cancel spinlock.
+ //
+ WDFQUEUE ReadQueue;
+
+ //
+ // This list head is used to contain the time ordered list
+ // of write requests. Access to this list is protected by
+ // the global cancel spinlock.
+ //
+ WDFQUEUE WriteQueue;
+
+ //
+ // This list head is used to contain the time ordered list
+ // of set and wait mask requests. Access to this list is protected by
+ // the global cancel spinlock.
+ //
+ WDFQUEUE MaskQueue;
+
+ //
+ // Holds the serialized list of purge requests.
+ //
+ WDFQUEUE PurgeQueue;
+
+ //
+ // This points to the request that is currently being processed
+ // for the read queue. This field is initialized by the open to
+ // NULL.
+ //
+ // This value is only set at dispatch level. It may be
+ // read at interrupt level.
+ //
+ WDFREQUEST CurrentReadRequest;
+
+ //
+ // This points to the request that is currently being processed
+ // for the write queue.
+ //
+ // This value is only set at dispatch level. It may be
+ // read at interrupt level.
+ //
+ WDFREQUEST CurrentWriteRequest;
+
+ //
+ // Points to the request that is currently being processed to
+ // affect the wait mask operations.
+ //
+ WDFREQUEST CurrentMaskRequest;
+
+ //
+ // Points to the request that is currently being processed to
+ // purge the read/write queues and buffers.
+ //
+ WDFREQUEST CurrentPurgeRequest;
+
+ //
+ // Points to the current request that is waiting on a comm event.
+ //
+ WDFREQUEST CurrentWaitRequest;
+
+ //
+ // Points to the request that is being used to send an immediate
+ // character.
+ //
+ WDFREQUEST CurrentImmediateRequest;
+
+ //
+ // Points to the request that is being used to count the number
+ // of characters received after an xoff (as currently defined
+ // by the IOCTL_SERIAL_XOFF_COUNTER ioctl) is sent.
+ //
+ WDFREQUEST CurrentXoffRequest;
+
+ //
+ // The base address for the set of device registers
+ // of the serial port.
+ //
+ PUCHAR Controller;
+ //
+ // This value holds the span (in units of bytes) of the register
+ // set controlling this port. This is constant over the life
+ // of the port.
+ //
+ ULONG SpanOfController;
+
+ //
+ // Address space
+ //
+
+ ULONG AddressSpace;
+
+ PREAD_PORT_UCHAR SerialReadUChar;
+ PWRITE_PORT_UCHAR SerialWriteUChar;
+
+ //
+ // Hold the clock rate input to the serial part.
+ //
+ ULONG ClockRate;
+
+ //
+ // The number of characters to push out if a fifo is present.
+ //
+ ULONG TxFifoAmount;
+
+ //
+ // Set to indicate that it is ok to share interrupts within the device.
+ //
+ ULONG PermitShare;
+
+
+ //
+ // Points to the interrupt object for used by this device.
+ //
+ WDFINTERRUPT WdfInterrupt;
+
+ //
+ // Translated vector
+ //
+ ULONG Vector;
+ //
+ // Translated Irql
+ //
+ KIRQL Irql;
+
+ KINTERRUPT_MODE InterruptMode;
+
+ KAFFINITY Affinity;
+
+ //
+ // This value is set by the read code to hold the time value
+ // used for read interval timing. We keep it in the extension
+ // so that the interval timer dpc routine determine if the
+ // interval time has passed for the IO.
+ //
+ LARGE_INTEGER IntervalTime;
+
+ //
+ // These two values hold the "constant" time that we should use
+ // to delay for the read interval time.
+ //
+ LARGE_INTEGER ShortIntervalAmount;
+ LARGE_INTEGER LongIntervalAmount;
+
+ //
+ // This holds the value that we use to determine if we should use
+ // the long interval delay or the short interval delay.
+ //
+ LARGE_INTEGER CutOverAmount;
+
+ //
+ // This holds the system time when we last time we had
+ // checked that we had actually read characters. Used
+ // for interval timing.
+ //
+ LARGE_INTEGER LastReadTime;
+
+
+ //
+ // This points the the delta time that we should use to
+ // delay for interval timing.
+ //
+ PLARGE_INTEGER IntervalTimeToUse;
+
+
+ //
+ // Set at intialization to indicate that on the current
+ // architecture we need to unmap the base register address
+ // when we unload the driver.
+ //
+ BOOLEAN UnMapRegisters;
+
+ //
+ // Holds the number of bytes remaining in the current write
+ // request.
+ //
+ // This location is only accessed while at interrupt level.
+ //
+ ULONG WriteLength;
+
+ //
+ // Holds a pointer to the current character to be sent in
+ // the current write.
+ //
+ // This location is only accessed while at interrupt level.
+ //
+ PUCHAR WriteCurrentChar;
+
+ //
+ // This is a buffer for the read processing.
+ //
+ // The buffer works as a ring. When the character is read from
+ // the device it will be place at the end of the ring.
+ //
+ // Characters are only placed in this buffer at interrupt level
+ // although character may be read at any level. The pointers
+ // that manage this buffer may not be updated except at interrupt
+ // level.
+ //
+ PUCHAR InterruptReadBuffer;
+
+ //
+ // This is a pointer to the first character of the buffer into
+ // which the interrupt service routine is copying characters.
+ //
+ PUCHAR ReadBufferBase;
+
+ //
+ // This is a count of the number of characters in the interrupt
+ // buffer. This value is set and read at interrupt level. Note
+ // that this value is only *incremented* at interrupt level so
+ // it is safe to read it at any level. When characters are
+ // copied out of the read buffer, this count is decremented by
+ // a routine that synchronizes with the ISR.
+ //
+ ULONG CharsInInterruptBuffer;
+
+ //
+ // Points to the first available position for a newly received
+ // character. This variable is only accessed at interrupt level and
+ // buffer initialization code.
+ //
+ PUCHAR CurrentCharSlot;
+
+ //
+ // This variable is used to contain the last available position
+ // in the read buffer. It is updated at open and at interrupt
+ // level when switching between the users buffer and the interrupt
+ // buffer.
+ //
+ PUCHAR LastCharSlot;
+
+ //
+ // This marks the first character that is available to satisfy
+ // a read request. Note that while this always points to valid
+ // memory, it may not point to a character that can be sent to
+ // the user. This can occur when the buffer is empty.
+ //
+ PUCHAR FirstReadableChar;
+
+ //
+ // Pointer to the lock variable returned for this extension when
+ // locking down the driver
+ //
+ PVOID LockPtr;
+
+
+ //
+ // This variable holds the size of whatever buffer we are currently
+ // using.
+ //
+ ULONG BufferSize;
+
+ //
+ // This variable holds .8 of BufferSize. We don't want to recalculate
+ // this real often - It's needed when so that an application can be
+ // "notified" that the buffer is getting full.
+ //
+ ULONG BufferSizePt8;
+
+ //
+ // This value holds the number of characters desired for a
+ // particular read. It is initially set by read length in the
+ // WDFREQUEST. It is decremented each time more characters are placed
+ // into the "users" buffer buy the code that reads characters
+ // out of the typeahead buffer into the users buffer. If the
+ // typeahead buffer is exhausted by the read, and the reads buffer
+ // is given to the isr to fill, this value is becomes meaningless.
+ //
+ ULONG NumberNeededForRead;
+
+ //
+ // This mask will hold the bitmask sent down via the set mask
+ // ioctl. It is used by the interrupt service routine to determine
+ // if the occurence of "events" (in the serial drivers understanding
+ // of the concept of an event) should be noted.
+ //
+ ULONG IsrWaitMask;
+
+ //
+ // This mask will always be a subset of the IsrWaitMask. While
+ // at device level, if an event occurs that is "marked" as interesting
+ // in the IsrWaitMask, the driver will turn on that bit in this
+ // history mask. The driver will then look to see if there is a
+ // request waiting for an event to occur. If there is one, it
+ // will copy the value of the history mask into the wait request, zero
+ // the history mask, and complete the wait request. If there is no
+ // waiting request, the driver will be satisfied with just recording
+ // that the event occured. If a wait request should be queued,
+ // the driver will look to see if the history mask is non-zero. If
+ // it is non-zero, the driver will copy the history mask into the
+ // request, zero the history mask, and then complete the request.
+ //
+ ULONG HistoryMask;
+
+ //
+ // This is a pointer to the where the history mask should be
+ // placed when completing a wait. It is only accessed at
+ // device level.
+ //
+ // We have a pointer here to assist us to synchronize completing a wait.
+ // If this is non-zero, then we have wait outstanding, and the isr still
+ // knows about it. We make this pointer null so that the isr won't
+ // attempt to complete the wait.
+ //
+ // We still keep a pointer around to the wait request, since the actual
+ // pointer to the wait request will be used for the "common" request completion
+ // path.
+ //
+ ULONG *IrpMaskLocation;
+
+ //
+ // This mask holds all of the reason that transmission
+ // is not proceeding. Normal transmission can not occur
+ // if this is non-zero.
+ //
+ // This is only written from interrupt level.
+ // This could be (but is not) read at any level.
+ //
+ ULONG TXHolding;
+
+ //
+ // This mask holds all of the reason that reception
+ // is not proceeding. Normal reception can not occur
+ // if this is non-zero.
+ //
+ // This is only written from interrupt level.
+ // This could be (but is not) read at any level.
+ //
+ ULONG RXHolding;
+
+ //
+ // This holds the reasons that the driver thinks it is in
+ // an error state.
+ //
+ // This is only written from interrupt level.
+ // This could be (but is not) read at any level.
+ //
+ ULONG ErrorWord;
+
+ //
+ // This keeps a total of the number of characters that
+ // are in all of the "write" irps that the driver knows
+ // about. It is only accessed with the cancel spinlock
+ // held.
+ //
+ ULONG TotalCharsQueued;
+
+ //
+ // This holds a count of the number of characters read
+ // the last time the interval timer dpc fired. It
+ // is a long (rather than a ulong) since the other read
+ // completion routines use negative values to indicate
+ // to the interval timer that it should complete the read
+ // if the interval timer DPC was lurking in some DPC queue when
+ // some other way to complete occurs.
+ //
+ LONG CountOnLastRead;
+
+ //
+ // This is a count of the number of characters read by the
+ // isr routine. It is *ONLY* written at isr level. We can
+ // read it at dispatch level.
+ //
+ ULONG ReadByIsr;
+
+ //
+ // This holds the current baud rate for the device.
+ //
+ ULONG CurrentBaud;
+
+ //
+ // This is the number of characters read since the XoffCounter
+ // was started. This variable is only accessed at device level.
+ // If it is greater than zero, it implies that there is an
+ // XoffCounter ioctl in the queue.
+ //
+ LONG CountSinceXoff;
+
+ //
+ // This ulong is incremented each time something trys to start
+ // the execution path that tries to lower the RTS line when
+ // doing transmit toggling. If it "bumps" into another path
+ // (indicated by a false return value from queueing a dpc
+ // and a TRUE return value tring to start a timer) it will
+ // decrement the count. These increments and decrements
+ // are all done at device level. Note that in the case
+ // of a bump while trying to start the timer, we have to
+ // go up to device level to do the decrement.
+ //
+ ULONG CountOfTryingToLowerRTS;
+
+ //
+ // This ULONG is used to keep track of the "named" (in ntddser.h)
+ // baud rates that this particular device supports.
+ //
+ ULONG SupportedBauds;
+
+ //
+ // Holds the timeout controls for the device. This value
+ // is set by the Ioctl processing.
+ //
+ // It should only be accessed under protection of the control
+ // lock since more than one request can be in the control dispatch
+ // routine at one time.
+ //
+ SERIAL_TIMEOUTS Timeouts;
+
+ //
+ // This holds the various characters that are used
+ // for replacement on errors and also for flow control.
+ //
+ // They are only set at interrupt level.
+ //
+ SERIAL_CHARS SpecialChars;
+
+ //
+ // This structure holds the handshake and control flow
+ // settings for the serial driver.
+ //
+ // It is only set at interrupt level. It can be
+ // be read at any level with the control lock held.
+ //
+ SERIAL_HANDFLOW HandFlow;
+
+
+ //
+ // Holds performance statistics that applications can query.
+ // Reset on each open. Only set at device level.
+ //
+ SERIALPERF_STATS PerfStats;
+
+ //
+ // This holds what we beleive to be the current value of
+ // the line control register.
+ //
+ // It should only be accessed under protection of the control
+ // lock since more than one request can be in the control dispatch
+ // routine at one time.
+ //
+ UCHAR LineControl;
+
+
+ //
+ // This is only accessed at interrupt level. It keeps track
+ // of whether the holding register is empty.
+ //
+ BOOLEAN HoldingEmpty;
+
+ //
+ // This variable is only accessed at interrupt level. It
+ // indicates that we want to transmit a character immediately.
+ // That is - in front of any characters that could be transmitting
+ // from a normal write.
+ //
+ BOOLEAN TransmitImmediate;
+
+ //
+ // This variable is only accessed at interrupt level. Whenever
+ // a wait is initiated this variable is set to false.
+ // Whenever any kind of character is written it is set to true.
+ // Whenever the write queue is found to be empty the code that
+ // is processing that completing request will synchonize with the interrupt.
+ // If this synchronization code finds that the variable is true and that
+ // there is a wait on the transmit queue being empty then it is
+ // certain that the queue was emptied and that it has happened since
+ // the wait was initiated.
+ //
+ BOOLEAN EmptiedTransmit;
+
+ //
+ // We keep the following values around so that we can connect
+ // to the interrupt and report resources after the configuration
+ // record is gone.
+ //
+
+ //
+ // We hold the character that should be transmitted immediately.
+ //
+ // Note that we can't use this to determine whether there is
+ // a character to send because the character to send could be
+ // zero.
+ //
+ UCHAR ImmediateChar;
+
+ //
+ // This holds the mask that will be used to mask off unwanted
+ // data bits of the received data (valid data bits can be 5,6,7,8)
+ // The mask will normally be 0xff. This is set while the control
+ // lock is held since it wouldn't have adverse effects on the
+ // isr if it is changed in the middle of reading characters.
+ // (What it would do to the app is another question - but then
+ // the app asked the driver to do it.)
+ //
+ UCHAR ValidDataMask;
+
+ //
+ // The application can turn on a mode,via the
+ // IOCTL_SERIAL_LSRMST_INSERT ioctl, that will cause the
+ // serial driver to insert the line status or the modem
+ // status into the RX stream. The parameter with the ioctl
+ // is a pointer to a UCHAR. If the value of the UCHAR is
+ // zero, then no insertion will ever take place. If the
+ // value of the UCHAR is non-zero (and not equal to the
+ // xon/xoff characters), then the serial driver will insert.
+ //
+ UCHAR EscapeChar;
+
+ //
+ // These two booleans are used to indicate to the isr transmit
+ // code that it should send the xon or xoff character. They are
+ // only accessed at open and at interrupt level.
+ //
+ BOOLEAN SendXonChar;
+ BOOLEAN SendXoffChar;
+
+ //
+ // This boolean will be true if a 16550 is present *and* enabled.
+ //
+ BOOLEAN FifoPresent;
+
+ //
+ // This is the water mark that the rxfifo should be
+ // set to when the fifo is turned on. This is not the actual
+ // value, but the encoded value that goes into the register.
+ //
+ UCHAR RxFifoTrigger;
+
+ //
+ // This points to a DPC used to complete write requests.
+ //
+ WDFDPC CompleteWriteDpc;
+
+ //
+ // This points to a DPC used to complete read requests.
+ //
+ WDFDPC CompleteReadDpc;
+
+
+ //
+ // This dpc is fired off if a comm error occurs. It will
+ // execute a dpc routine that will cancel all pending reads
+ // and writes.
+ //
+ WDFDPC CommErrorDpc;
+
+ //
+ // This dpc is fired off if an event occurs and there was
+ // a request waiting on that event. A dpc routine will execute
+ // that completes the request.
+ //
+ WDFDPC CommWaitDpc;
+
+ //
+ // This dpc is fired off when the transmit immediate char
+ // character is given to the hardware. It will simply complete
+ // the request.
+ //
+ WDFDPC CompleteImmediateDpc;
+
+ //
+ // This dpc is fired off if the xoff counter actually runs down
+ // to zero.
+ //
+ WDFDPC XoffCountCompleteDpc;
+
+ //
+ // This dpc is fired off only from device level to start off
+ // a timer that will queue a dpc to check if the RTS line
+ // should be lowered when we are doing transmit toggling.
+ //
+ WDFDPC StartTimerLowerRTSDpc;
+
+ //
+ // This timer used to handle total read request timing.
+ //
+ WDFTIMER ReadRequestTotalTimer;
+
+ //
+ // This timer used to handle interval read request timing.
+ //
+ WDFTIMER ReadRequestIntervalTimer;
+
+ //
+ // This timer used to handle total write request timing.
+ //
+ WDFTIMER WriteRequestTotalTimer;
+
+ //
+ // This is timer structure used to handle total time request timing.
+ //
+ WDFTIMER ImmediateTotalTimer;
+
+ //
+ // This timer is used to timeout the xoff counter io.
+ //
+ WDFTIMER XoffCountTimer;
+
+ //
+ // This timer is used to invoke a dpc one character time
+ // after the timer is set. That dpc will be used to check
+ // whether we should lower the RTS line if we are doing
+ // transmit toggling.
+ //
+ WDFTIMER LowerRTSTimer;
+
+ //
+ // WMI Information
+ //
+
+ //
+ // WMI Comm Data
+ //
+
+ SERIAL_WMI_COMM_DATA WmiCommData;
+
+ //
+ // WMI HW Data
+ //
+
+ SERIAL_WMI_HW_DATA WmiHwData;
+
+ //
+ // WMI Performance Data
+ //
+
+ SERIAL_WMI_PERF_DATA WmiPerfData;
+
+} SERIAL_DEVICE_EXTENSION,*PSERIAL_DEVICE_EXTENSION;
+
+WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(SERIAL_DEVICE_EXTENSION,
+ SerialGetDeviceExtension)
+
+//
+// This is the scratch area for every request.
+// We will copy some of the frequently used information of the request
+// into our context area so that way we don't have to call WdfRequestGetParams
+// function everytime.
+//
+typedef struct _REQUEST_CONTEXT {
+ ULONG_PTR Information;
+ NTSTATUS Status;
+ ULONG Length;
+ PVOID RefCount;
+ PVOID SystemBuffer;
+ UCHAR MajorFunction;
+ PFN_WDF_REQUEST_CANCEL CancelRoutine;
+ BOOLEAN Cancelled;
+ PVOID Type3InputBuffer;
+ PSERIAL_DEVICE_EXTENSION Extension;
+ ULONG IoctlCode;
+ BOOLEAN MarkCancelableOnResume;
+} REQUEST_CONTEXT, *PREQUEST_CONTEXT;
+
+
+WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(REQUEST_CONTEXT,
+ SerialGetRequestContext)
+
+
+//
+// This is the Interrupt context for the Serial device. This structure is used
+// for keeping track of whether the Interrupt is connected or not.
+//
+typedef struct _SERIAL_INTERRUPT_CONTEXT {
+
+ //
+ // This boolean value indicates whether Interrupt is connected.
+ //
+ BOOLEAN IsInterruptConnected;
+
+ //
+ // This lock is used to synchronize the file close logic and
+ // the Surprise Removal logic. When a surprise remove happens,
+ // the device interrupts are disabled. When this occurs, the
+ // file close logic should not attempt to use the interrupt
+ // object.
+ //
+ WDFWAITLOCK InterruptStateLock;
+
+} SERIAL_INTERRUPT_CONTEXT, *PSERIAL_INTERRUPT_CONTEXT;
+
+WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(SERIAL_INTERRUPT_CONTEXT,
+ SerialGetInterruptContext)
+
+
+#define SERIAL_FLAGS_CLEAR 0x0L
+#define SERIAL_FLAGS_STARTED 0x1L
+#define SERIAL_FLAGS_STOPPED 0x2L
+#define SERIAL_FLAGS_BROKENHW 0x4L
+#define SERIAL_FLAGS_LEGACY_ENUMED 0x8L
+
+
+__inline
+UCHAR
+SerialReadPortUChar (
+ IN UCHAR * x
+ )
+{
+ return READ_PORT_UCHAR (x);
+}
+__inline
+VOID
+SerialWritePortUChar (
+ IN UCHAR * x,
+ IN UCHAR y
+ )
+{
+ WRITE_PORT_UCHAR (x,y);
+}
+
+__inline
+UCHAR
+SerialReadRegisterUChar (
+ IN UCHAR * x
+ )
+{
+ return READ_REGISTER_UCHAR (x);
+}
+
+__inline
+VOID
+SerialWriteRegisterUChar (
+ IN UCHAR * x,
+ IN UCHAR y
+ )
+{
+ WRITE_REGISTER_UCHAR (x,y);
+}
+
+
+
+//
+// Sets the divisor latch register. The divisor latch register
+// is used to control the baud rate of the 8250.
+//
+// As with all of these routines it is assumed that it is called
+// at a safe point to access the hardware registers. In addition
+// it also assumes that the data is correct.
+//
+// Arguments:
+//
+// BaseAddress - A pointer to the address from which the hardware
+// device registers are located.
+//
+// DesiredDivisor - The value to which the divisor latch register should
+// be set.
+//
+#define WRITE_DIVISOR_LATCH(Extension, BaseAddress,DesiredDivisor) \
+do \
+{ \
+ PUCHAR Address = BaseAddress; \
+ SHORT Divisor = DesiredDivisor; \
+ UCHAR LineControl; \
+ LineControl = Extension->SerialReadUChar(Address+LINE_CONTROL_REGISTER); \
+ Extension->SerialWriteUChar( \
+ Address+LINE_CONTROL_REGISTER, \
+ (UCHAR)(LineControl | SERIAL_LCR_DLAB) \
+ ); \
+ Extension->SerialWriteUChar( \
+ Address+DIVISOR_LATCH_LSB, \
+ (UCHAR)(Divisor & 0xff) \
+ ); \
+ Extension->SerialWriteUChar( \
+ Address+DIVISOR_LATCH_MSB, \
+ (UCHAR)((Divisor & 0xff00) >> 8) \
+ ); \
+ Extension->SerialWriteUChar( \
+ Address+LINE_CONTROL_REGISTER, \
+ LineControl \
+ ); \
+} WHILE (0)
+
+//
+// Reads the divisor latch register. The divisor latch register
+// is used to control the baud rate of the 8250.
+//
+// As with all of these routines it is assumed that it is called
+// at a safe point to access the hardware registers. In addition
+// it also assumes that the data is correct.
+//
+// Arguments:
+//
+// BaseAddress - A pointer to the address from which the hardware
+// device registers are located.
+//
+// DesiredDivisor - A pointer to the 2 byte word which will contain
+// the value of the divisor.
+//
+#define READ_DIVISOR_LATCH(Extension, BaseAddress,PDesiredDivisor) \
+do \
+{ \
+ PUCHAR Address = BaseAddress; \
+ PSHORT PDivisor = PDesiredDivisor; \
+ UCHAR LineControl; \
+ UCHAR Lsb; \
+ UCHAR Msb; \
+ LineControl = Extension->SerialReadUChar(Address+LINE_CONTROL_REGISTER); \
+ Extension->SerialWriteUChar( \
+ Address+LINE_CONTROL_REGISTER, \
+ (UCHAR)(LineControl | SERIAL_LCR_DLAB) \
+ ); \
+ Lsb = Extension->SerialReadUChar(Address+DIVISOR_LATCH_LSB); \
+ Msb = Extension->SerialReadUChar(Address+DIVISOR_LATCH_MSB); \
+ *PDivisor = Lsb; \
+ *PDivisor = *PDivisor | (((USHORT)Msb) << 8); \
+ Extension->SerialWriteUChar( \
+ Address+LINE_CONTROL_REGISTER, \
+ LineControl \
+ ); \
+} WHILE (0)
+
+//
+// This macro reads the interrupt enable register.
+//
+// Arguments:
+//
+// BaseAddress - A pointer to the address from which the hardware
+// device registers are located.
+//
+#define READ_INTERRUPT_ENABLE(Extension, BaseAddress) \
+ (Extension->SerialReadUChar((BaseAddress)+INTERRUPT_ENABLE_REGISTER))
+
+//
+// This macro writes the interrupt enable register.
+//
+// Arguments:
+//
+// BaseAddress - A pointer to the address from which the hardware
+// device registers are located.
+//
+// Values - The values to write to the interrupt enable register.
+//
+#define WRITE_INTERRUPT_ENABLE(Extension, BaseAddress,Values) \
+do \
+{ \
+ Extension->SerialWriteUChar( \
+ BaseAddress+INTERRUPT_ENABLE_REGISTER, \
+ Values \
+ ); \
+} WHILE (0)
+
+//
+// This macro disables all interrupts on the hardware.
+//
+// Arguments:
+//
+// BaseAddress - A pointer to the address from which the hardware
+// device registers are located.
+//
+//
+#define DISABLE_ALL_INTERRUPTS(Extension, BaseAddress) \
+do \
+{ \
+ WRITE_INTERRUPT_ENABLE(Extension, BaseAddress,0); \
+} WHILE (0)
+
+//
+// This macro enables all interrupts on the hardware.
+//
+// Arguments:
+//
+// BaseAddress - A pointer to the address from which the hardware
+// device registers are located.
+//
+//
+#define ENABLE_ALL_INTERRUPTS(Extension, BaseAddress) \
+do \
+{ \
+ \
+ WRITE_INTERRUPT_ENABLE( \
+ (Extension), (BaseAddress), \
+ (UCHAR)(SERIAL_IER_RDA | SERIAL_IER_THR | \
+ SERIAL_IER_RLS | SERIAL_IER_MS) \
+ ); \
+ \
+} WHILE (0)
+
+//
+// This macro reads the interrupt identification register
+//
+// Arguments:
+//
+// BaseAddress - A pointer to the address from which the hardware
+// device registers are located.
+//
+// Note that this routine potententially quites a transmitter
+// empty interrupt. This is because one way that the transmitter
+// empty interrupt is cleared is to simply read the interrupt id
+// register.
+//
+//
+#define READ_INTERRUPT_ID_REG(Extension, BaseAddress) \
+ (Extension->SerialReadUChar((BaseAddress)+INTERRUPT_IDENT_REGISTER))
+
+//
+// This macro reads the modem control register
+//
+// Arguments:
+//
+// BaseAddress - A pointer to the address from which the hardware
+// device registers are located.
+//
+//
+#define READ_MODEM_CONTROL(Extension, BaseAddress) \
+ (Extension->SerialReadUChar((BaseAddress)+MODEM_CONTROL_REGISTER))
+
+//
+// This macro reads the modem status register
+//
+// Arguments:
+//
+// BaseAddress - A pointer to the address from which the hardware
+// device registers are located.
+//
+//
+#define READ_MODEM_STATUS(Extension, BaseAddress) \
+ (Extension->SerialReadUChar((BaseAddress)+MODEM_STATUS_REGISTER))
+
+//
+// This macro reads a value out of the receive buffer
+//
+// Arguments:
+//
+// BaseAddress - A pointer to the address from which the hardware
+// device registers are located.
+//
+//
+#define READ_RECEIVE_BUFFER(Extension, BaseAddress) \
+ (Extension->SerialReadUChar((BaseAddress)+RECEIVE_BUFFER_REGISTER))
+
+//
+// This macro reads the line status register
+//
+// Arguments:
+//
+// BaseAddress - A pointer to the address from which the hardware
+// device registers are located.
+//
+//
+#define READ_LINE_STATUS(Extension, BaseAddress) \
+ (Extension->SerialReadUChar((BaseAddress)+LINE_STATUS_REGISTER))
+
+//
+// This macro writes the line control register
+//
+// Arguments:
+//
+// BaseAddress - A pointer to the address from which the hardware
+// device registers are located.
+//
+//
+#define WRITE_LINE_CONTROL(Extension, BaseAddress,NewLineControl) \
+do \
+{ \
+ Extension->SerialWriteUChar( \
+ (BaseAddress)+LINE_CONTROL_REGISTER, \
+ (NewLineControl) \
+ ); \
+} WHILE (0)
+
+//
+// This macro reads the line control register
+//
+// Arguments:
+//
+// BaseAddress - A pointer to the address from which the hardware
+// device registers are located.
+//
+//
+#define READ_LINE_CONTROL(Extension, BaseAddress) \
+ (Extension->SerialReadUChar((BaseAddress)+LINE_CONTROL_REGISTER))
+
+
+//
+// This macro writes to the transmit register
+//
+// Arguments:
+//
+// BaseAddress - A pointer to the address from which the hardware
+// device registers are located.
+//
+// TransmitChar - The character to send down the wire.
+//
+//
+#define WRITE_TRANSMIT_HOLDING(Extension, BaseAddress,TransmitChar) \
+do \
+{ \
+ Extension->SerialWriteUChar( \
+ (BaseAddress)+TRANSMIT_HOLDING_REGISTER, \
+ (TransmitChar) \
+ ); \
+} WHILE (0)
+
+//
+// This macro writes to the transmit FIFO register
+//
+// Arguments:
+//
+// BaseAddress - A pointer to the address from which the hardware
+// device registers are located.
+//
+// TransmitChars - Pointer to the characters to send down the wire.
+//
+// TxN - number of charactes to send.
+//
+//
+#define WRITE_TRANSMIT_FIFO_HOLDING(Extension, BaseAddress,TransmitChars,TxN) \
+do \
+{ \
+ WRITE_PORT_BUFFER_UCHAR( \
+ (BaseAddress)+TRANSMIT_HOLDING_REGISTER, \
+ (TransmitChars), \
+ (TxN) \
+ ); \
+} WHILE (0)
+
+//
+// This macro writes to the control register
+//
+// Arguments:
+//
+// BaseAddress - A pointer to the address from which the hardware
+// device registers are located.
+//
+// ControlValue - The value to set the fifo control register too.
+//
+//
+#define WRITE_FIFO_CONTROL(Extension, BaseAddress,ControlValue) \
+do \
+{ \
+ Extension->SerialWriteUChar( \
+ (BaseAddress)+FIFO_CONTROL_REGISTER, \
+ (ControlValue) \
+ ); \
+} WHILE (0)
+
+//
+// This macro writes to the modem control register
+//
+// Arguments:
+//
+// BaseAddress - A pointer to the address from which the hardware
+// device registers are located.
+//
+// ModemControl - The control bits to send to the modem control.
+//
+//
+#define WRITE_MODEM_CONTROL(Extension, BaseAddress,ModemControl) \
+do \
+{ \
+ Extension->SerialWriteUChar( \
+ (BaseAddress)+MODEM_CONTROL_REGISTER, \
+ (ModemControl) \
+ ); \
+} WHILE (0)
+
+#define WRITE_INTERRUPT_STATUS(Extension, BaseAddress,Status) \
+do \
+{ \
+ Extension->SerialWriteUChar(BaseAddress, Status); \
+} WHILE (0)
+
+
+//
+// This macro reads the interrupt status register
+//
+// Arguments:
+//
+// BaseAddress - A pointer to the address from which the hardware
+// device registers are located. BaseAddress is gotten
+// from PSERIAL_MULTIPORT_DISPATCH->InterruptStatus which
+// already has the complete address
+//
+// AddressSpace - Flag indicating where port is located, MMIO or IO
+// space
+//
+//
+#define READ_INTERRUPT_STATUS(Extension, BaseAddress) \
+ Extension->SerialReadUChar(BaseAddress))
+
+//
+// We use this to query into the registry as to whether we
+// should break at driver entry.
+//
+
+extern SERIAL_FIRMWARE_DATA driverDefaults;
+
+
+//
+// This is exported from the kernel. It is used to point
+// to the address that the kernel debugger is using.
+//
+
+extern PUCHAR *KdComPortInUse;
+
+
+typedef enum _SERIAL_MEM_COMPARES {
+ AddressesAreEqual,
+ AddressesOverlap,
+ AddressesAreDisjoint
+ } SERIAL_MEM_COMPARES,*PSERIAL_MEM_COMPARES;
+
+#define SERIAL_BAUD_INVALID 0xFFFFFFFF
+
+typedef struct _SUPPORTED_BAUD_RATES {
+ UINT32 BaudRate;
+ ULONG Mask;
+}SUPPORTED_BAUD_RATES;
+
diff --git a/serial/serial/serial.inx b/serial/serial/serial.inx
new file mode 100644
index 00000000..e71feb31
--- /dev/null
+++ b/serial/serial/serial.inx
@@ -0,0 +1,111 @@
+;/*++
+;
+;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:
+;
+; SERIAL.INF
+;
+;Abstract:
+; INF file for the WDF serial driver sample
+;
+;--*/
+
+[version]
+signature="$CHICAGO$"
+Class=Ports
+ClassGuid={4D36E978-E325-11CE-BFC1-08002BE10318}
+Provider=%MSFT%
+CatalogFile=KmdfSamples.cat
+
+
+; ================= Device Install section =====================
+
+[ControlFlags]
+ExcludeFromSelect=*
+
+[Manufacturer]
+%MSFT%=MSFT,NT$ARCH$
+
+[SourceDisksFiles]
+wdfserial.sys=1
+
+[SourceDisksNames]
+1=%DISK_NAME%,
+
+[DestinationDirs]
+DefaultDestDir = 12
+
+; For XP and later
+[MSFT.NT$ARCH$]
+; DisplayName Section DeviceId
+; ----------- ------- --------
+%PNP0500.DevDesc%= Serial_Inst, *PNP0500, *PNP0501 ; Communications Port
+%PNP0501.DevDesc%= Serial_Inst, *PNP0501, *PNP0500 ; Communications Port
+
+
+[Serial_Inst.NT]
+CopyFiles=Serial.CopyFiles
+
+[Serial.CopyFiles]
+wdfserial.sys
+
+[Serial_Inst.NT.Services]
+AddService=Serial,0x00000002,Serial_Service
+
+[Serial_Service]
+DisplayName = %Serial.SVCDESC%
+ServiceType = 1 ; SERVICE_KERNEL_DRIVER
+StartType = 3 ; SERVICE_DEMAND_START
+ErrorControl = 1 ; SERVICE_ERROR_NORMAL
+ServiceBinary = %12%\wdfserial.sys
+
+[Serial_Inst.NT.HW]
+Addreg=Serial_Inst.NT.HW.AddReg
+
+[Serial_Inst.NT.HW.AddReg]
+;
+; By default, WDF device is protected with SDDL_DEVOBJ_SYS_ALL_ADM_ALL which means
+; only operating system and administrators can access the COM1 device. Let's change
+; it to allow everyone to have read/write access to it. Please refer to WDK section
+; "SDDL for Device Objects" for detailed description on this.
+;
+HKR,,Security,,"D:P(A;;GA;;;SY)(A;;GA;;;BA)(A;;GRGW;;;WD)"
+;
+; apply FILE_DEVICE_SECURE_OPEN to the whole devnode: Use same security checks on relative opens
+;
+HKR,,DeviceCharacteristics,0x10001,0x100
+
+;-------------- WDF Coinstaller installation
+[DestinationDirs]
+CoInstaller_CopyFiles = 11
+
+[Serial_Inst.NT.CoInstallers]
+AddReg=CoInstaller_AddReg
+CopyFiles=CoInstaller_CopyFiles
+
+[CoInstaller_CopyFiles]
+WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll
+
+[SourceDisksFiles]
+WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll=1 ; make sure the number matches with SourceDisksNames
+
+[CoInstaller_AddReg]
+HKR,,CoInstallers32,0x00010000, "WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll,WdfCoInstaller"
+
+[Serial_Inst.NT.Wdf]
+KmdfService = Serial, Serial_wdfsect
+[Serial_wdfsect]
+KmdfLibraryVersion = $KMDFVERSION$
+
+[Strings]
+MSFT = "Microsoft"
+Serial.SVCDESC = "WDF Serial driver"
+PNP0500.DevDesc = "Communications Port"
+PNP0501.DevDesc = "Communications Port"
+DISK_NAME = "Serial Install Disk"
diff --git a/serial/serial/serial.rc b/serial/serial/serial.rc
new file mode 100644
index 00000000..0afa03e5
--- /dev/null
+++ b/serial/serial/serial.rc
@@ -0,0 +1,14 @@
+#include <windows.h>
+
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_DRV
+#define VER_FILESUBTYPE VFT2_DRV_SYSTEM
+#define VER_FILEDESCRIPTION_STR "Serial Device Driver"
+#define VER_INTERNALNAME_STR "serial.sys"
+#define VER_ORIGINALFILENAME_STR "serial.sys"
+
+#include "common.ver"
+
+#include "serlog.rc"
+
diff --git a/serial/serial/serial.sln b/serial/serial/serial.sln
new file mode 100644
index 00000000..b5cea148
--- /dev/null
+++ b/serial/serial/serial.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}") = "wdfserial", "wdfserial.vcxproj", "{B4050279-1352-4D9E-9CF6-6409E8330DF1}"
+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
+ {B4050279-1352-4D9E-9CF6-6409E8330DF1}.Debug|Win32.ActiveCfg = Debug|Win32
+ {B4050279-1352-4D9E-9CF6-6409E8330DF1}.Debug|Win32.Build.0 = Debug|Win32
+ {B4050279-1352-4D9E-9CF6-6409E8330DF1}.Release|Win32.ActiveCfg = Release|Win32
+ {B4050279-1352-4D9E-9CF6-6409E8330DF1}.Release|Win32.Build.0 = Release|Win32
+ {B4050279-1352-4D9E-9CF6-6409E8330DF1}.Debug|x64.ActiveCfg = Debug|x64
+ {B4050279-1352-4D9E-9CF6-6409E8330DF1}.Debug|x64.Build.0 = Debug|x64
+ {B4050279-1352-4D9E-9CF6-6409E8330DF1}.Release|x64.ActiveCfg = Release|x64
+ {B4050279-1352-4D9E-9CF6-6409E8330DF1}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/serial/serial/serialp.h b/serial/serial/serialp.h
new file mode 100644
index 00000000..0a04c0f5
--- /dev/null
+++ b/serial/serial/serialp.h
@@ -0,0 +1,596 @@
+/*++
+
+Copyright (c) Microsoft Corporation
+
+Module Name :
+
+ serialp.h
+
+Abstract:
+
+ Prototypes and macros that are used throughout the driver.
+
+--*/
+
+//-----------------------------------------------------------------------------
+// 4127 -- Conditional Expression is Constant warning
+//-----------------------------------------------------------------------------
+#define WHILE(constant) \
+__pragma(warning(suppress: 4127)) while(constant)
+
+typedef
+VOID
+(*PSERIAL_START_ROUTINE) (
+ IN PSERIAL_DEVICE_EXTENSION
+ );
+
+typedef
+VOID
+(*PSERIAL_GET_NEXT_ROUTINE) (
+ IN WDFREQUEST *CurrentOpRequest,
+ IN WDFQUEUE QueueToProcess,
+ OUT WDFREQUEST *NewRequest,
+ IN BOOLEAN CompleteCurrent,
+ PSERIAL_DEVICE_EXTENSION Extension
+ );
+
+DRIVER_INITIALIZE DriverEntry;
+
+EVT_WDF_DRIVER_DEVICE_ADD SerialEvtDeviceAdd;
+EVT_WDF_OBJECT_CONTEXT_CLEANUP SerialEvtDriverContextCleanup;
+EVT_WDF_DEVICE_CONTEXT_CLEANUP SerialEvtDeviceContextCleanup;
+
+EVT_WDF_DEVICE_D0_ENTRY SerialEvtDeviceD0Entry;
+EVT_WDF_DEVICE_D0_EXIT SerialEvtDeviceD0Exit;
+EVT_WDF_DEVICE_D0_ENTRY_POST_INTERRUPTS_ENABLED SerialEvtDeviceD0EntryPostInterruptsEnabled;
+EVT_WDF_DEVICE_D0_EXIT_PRE_INTERRUPTS_DISABLED SerialEvtDeviceD0ExitPreInterruptsDisabled;
+EVT_WDF_DEVICE_PREPARE_HARDWARE SerialEvtPrepareHardware;
+EVT_WDF_DEVICE_RELEASE_HARDWARE SerialEvtReleaseHardware;
+
+EVT_WDF_DEVICE_FILE_CREATE SerialEvtDeviceFileCreate;
+EVT_WDF_FILE_CLOSE SerialEvtFileClose;
+
+EVT_WDF_IO_QUEUE_IO_READ SerialEvtIoRead;
+EVT_WDF_IO_QUEUE_IO_WRITE SerialEvtIoWrite;
+EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL SerialEvtIoDeviceControl;
+EVT_WDF_IO_QUEUE_IO_INTERNAL_DEVICE_CONTROL SerialEvtIoInternalDeviceControl;
+EVT_WDF_IO_QUEUE_IO_CANCELED_ON_QUEUE SerialEvtCanceledOnQueue;
+EVT_WDF_IO_QUEUE_IO_STOP SerialEvtIoStop;
+EVT_WDF_IO_QUEUE_IO_RESUME SerialEvtIoResume;
+
+EVT_WDF_INTERRUPT_ENABLE SerialEvtInterruptEnable;
+EVT_WDF_INTERRUPT_DISABLE SerialEvtInterruptDisable;
+
+EVT_WDF_DPC SerialCompleteRead;
+EVT_WDF_DPC SerialCompleteWrite;
+EVT_WDF_DPC SerialCommError;
+EVT_WDF_DPC SerialCompleteImmediate;
+EVT_WDF_DPC SerialCompleteXoff;
+EVT_WDF_DPC SerialCompleteWait;
+EVT_WDF_DPC SerialStartTimerLowerRTS;
+
+EVT_WDF_TIMER SerialReadTimeout;
+EVT_WDF_TIMER SerialIntervalReadTimeout;
+EVT_WDF_TIMER SerialWriteTimeout;
+EVT_WDF_TIMER SerialTimeoutImmediate;
+EVT_WDF_TIMER SerialTimeoutXoff;
+EVT_WDF_TIMER SerialInvokePerhapsLowerRTS;
+
+VOID
+SerialStartRead(
+ IN PSERIAL_DEVICE_EXTENSION Extension
+ );
+
+VOID
+SerialStartWrite(
+ IN PSERIAL_DEVICE_EXTENSION Extension
+ );
+
+VOID
+SerialStartMask(
+ IN PSERIAL_DEVICE_EXTENSION Extension
+ );
+
+VOID
+SerialStartImmediate(
+ IN PSERIAL_DEVICE_EXTENSION Extension
+ );
+
+VOID
+SerialStartPurge(
+ IN PSERIAL_DEVICE_EXTENSION Extension
+ );
+
+VOID
+SerialGetNextWrite(
+ IN WDFREQUEST *CurrentOpRequest,
+ IN WDFQUEUE QueueToProcess,
+ IN WDFREQUEST *NewRequest,
+ IN BOOLEAN CompleteCurrent,
+ IN PSERIAL_DEVICE_EXTENSION Extension
+ );
+
+EVT_WDFDEVICE_WDM_IRP_PREPROCESS SerialWdmDeviceFileCreate;
+EVT_WDFDEVICE_WDM_IRP_PREPROCESS SerialWdmFileClose;
+EVT_WDFDEVICE_WDM_IRP_PREPROCESS SerialFlush;
+
+EVT_WDFDEVICE_WDM_IRP_PREPROCESS SerialQueryInformationFile;
+EVT_WDFDEVICE_WDM_IRP_PREPROCESS SerialSetInformationFile;
+
+NTSTATUS
+SerialDeviceFileCreateWorker (
+ IN WDFDEVICE Device
+ );
+
+
+VOID
+SerialFileCloseWorker(
+ IN WDFDEVICE Device
+ );
+
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialProcessEmptyTransmit;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialSetDTR;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialClrDTR;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialSetRTS;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialClrRTS;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialSetBaud;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialSetLineControl;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialSetHandFlow;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialTurnOnBreak;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialTurnOffBreak;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialPretendXoff;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialPretendXon;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialReset;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialPerhapsLowerRTS;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialMarkOpen;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialMarkClose;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialGetStats;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialClearStats;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialSetChars;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialSetMCRContents;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialGetMCRContents;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialSetFCRContents;
+
+BOOLEAN
+SerialSetupNewHandFlow(
+ IN PSERIAL_DEVICE_EXTENSION Extension,
+ IN PSERIAL_HANDFLOW NewHandFlow
+ );
+
+
+VOID
+SerialHandleReducedIntBuffer(
+ IN PSERIAL_DEVICE_EXTENSION Extension
+ );
+
+VOID
+SerialProdXonXoff(
+ IN PSERIAL_DEVICE_EXTENSION Extension,
+ IN BOOLEAN SendXon
+ );
+
+EVT_WDF_REQUEST_CANCEL SerialCancelWait;
+
+
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialPurgeInterruptBuff;
+
+VOID
+SerialPurgeRequests(
+ IN WDFQUEUE QueueToClean,
+ IN WDFREQUEST *CurrentOpRequest
+ );
+
+VOID
+SerialFlushRequests(
+ IN WDFQUEUE QueueToClean,
+ IN WDFREQUEST *CurrentOpRequest
+ );
+
+VOID
+SerialGetNextRequest(
+ IN WDFREQUEST *CurrentOpRequest,
+ IN WDFQUEUE QueueToProcess,
+ OUT WDFREQUEST *NextIrp,
+ IN BOOLEAN CompleteCurrent,
+ IN PSERIAL_DEVICE_EXTENSION extension
+ );
+
+
+VOID
+SerialTryToCompleteCurrent(
+ IN PSERIAL_DEVICE_EXTENSION Extension,
+ IN PFN_WDF_INTERRUPT_SYNCHRONIZE SynchRoutine OPTIONAL,
+ IN NTSTATUS StatusToUse,
+ IN WDFREQUEST *CurrentOpRequest,
+ IN WDFQUEUE QueueToProcess,
+ IN WDFTIMER IntervalTimer,
+ IN WDFTIMER TotalTimer,
+ IN PSERIAL_START_ROUTINE Starter,
+ IN PSERIAL_GET_NEXT_ROUTINE GetNextIrp,
+ IN LONG RefType
+ );
+
+VOID
+SerialStartOrQueue(
+ IN PSERIAL_DEVICE_EXTENSION Extension,
+ IN WDFREQUEST Request,
+ IN WDFQUEUE QueueToExamine,
+ IN WDFREQUEST *CurrentOpRequest,
+ IN PSERIAL_START_ROUTINE Starter
+ );
+
+NTSTATUS
+SerialCompleteIfError(
+ PSERIAL_DEVICE_EXTENSION extension,
+ WDFREQUEST Request
+ );
+
+ULONG
+SerialHandleModemUpdate(
+ IN PSERIAL_DEVICE_EXTENSION Extension,
+ IN BOOLEAN DoingTX
+ );
+
+
+EVT_WDF_INTERRUPT_ISR SerialISR;
+
+NTSTATUS
+SerialGetDivisorFromBaud(
+ IN ULONG ClockRate,
+ IN LONG DesiredBaud,
+ OUT PSHORT AppropriateDivisor
+ );
+
+VOID
+SerialCleanupDevice(
+ IN PSERIAL_DEVICE_EXTENSION Extension
+ );
+
+UCHAR
+SerialProcessLSR(
+ IN PSERIAL_DEVICE_EXTENSION Extension
+ );
+
+LARGE_INTEGER
+SerialGetCharTime(
+ IN PSERIAL_DEVICE_EXTENSION Extension
+ );
+
+
+VOID
+SerialPutChar(
+ IN PSERIAL_DEVICE_EXTENSION Extension,
+ IN UCHAR CharToPut
+ );
+
+NTSTATUS
+SerialGetConfigDefaults(
+ IN PSERIAL_FIRMWARE_DATA DriverDefaultsPtr,
+ IN WDFDRIVER Driver
+ );
+
+VOID
+SerialGetProperties(
+ IN PSERIAL_DEVICE_EXTENSION Extension,
+ IN PSERIAL_COMMPROP Properties
+ );
+
+VOID
+SerialLogError(
+ _In_ PDRIVER_OBJECT DriverObject,
+ _In_opt_ PDEVICE_OBJECT DeviceObject,
+ _In_ PHYSICAL_ADDRESS P1,
+ _In_ PHYSICAL_ADDRESS P2,
+ _In_ ULONG SequenceNumber,
+ _In_ UCHAR MajorFunctionCode,
+ _In_ UCHAR RetryCount,
+ _In_ ULONG UniqueErrorValue,
+ _In_ NTSTATUS FinalStatus,
+ _In_ NTSTATUS SpecificIOStatus,
+ _In_ ULONG LengthOfInsert1,
+ _In_reads_bytes_opt_(LengthOfInsert1) PWCHAR Insert1,
+ _In_ ULONG LengthOfInsert2,
+ _In_reads_bytes_opt_(LengthOfInsert2) PWCHAR Insert2
+ );
+
+NTSTATUS
+SerialMapHWResources(
+ IN WDFDEVICE Device,
+ IN WDFCMRESLIST PResList,
+ IN WDFCMRESLIST PTrResList,
+ OUT PCONFIG_DATA PConfig
+ );
+
+VOID
+SerialUnmapHWResources(
+ IN PSERIAL_DEVICE_EXTENSION PDevExt
+ );
+
+BOOLEAN
+SerialGetRegistryKeyValue (
+ IN WDFDEVICE WdfDevice,
+ _In_ PCWSTR Name,
+ OUT PULONG Value
+ );
+
+
+BOOLEAN
+SerialPutRegistryKeyValue (
+ IN WDFDEVICE WdfDevice,
+ _In_ PCWSTR Name,
+ IN ULONG Value
+ );
+
+NTSTATUS
+SerialInitController(
+ IN PSERIAL_DEVICE_EXTENSION pDevExt,
+ IN PCONFIG_DATA PConfigData
+ );
+
+BOOLEAN
+SerialCIsrSw(
+ IN WDFINTERRUPT Interrupt,
+ IN ULONG MessageID
+ );
+
+NTSTATUS
+SerialDoExternalNaming(
+ IN PSERIAL_DEVICE_EXTENSION PDevExt
+ );
+
+PVOID
+SerialGetMappedAddress(
+ PHYSICAL_ADDRESS IoAddress,
+ ULONG NumberOfBytes,
+ ULONG AddressSpace,
+ PBOOLEAN MappedAddress
+ );
+
+BOOLEAN
+SerialDoesPortExist(
+ IN PSERIAL_DEVICE_EXTENSION Extension,
+ PUNICODE_STRING InsertString,
+ IN ULONG ForceFifo,
+ IN ULONG LogFifo
+ );
+
+SERIAL_MEM_COMPARES
+SerialMemCompare(
+ IN PHYSICAL_ADDRESS A,
+ IN ULONG SpanOfA,
+ IN PHYSICAL_ADDRESS B,
+ IN ULONG SpanOfB
+ );
+
+VOID
+SerialUndoExternalNaming(
+ IN PSERIAL_DEVICE_EXTENSION Extension
+ );
+
+VOID
+SerialReleaseResources(
+ IN PSERIAL_DEVICE_EXTENSION PDevExt
+ );
+
+VOID
+SerialPurgePendingRequests(
+ PSERIAL_DEVICE_EXTENSION pDevExt
+ );
+
+VOID
+SerialDisableUART(
+ IN PVOID Context
+ );
+
+VOID
+SerialDrainUART(
+ IN PSERIAL_DEVICE_EXTENSION PDevExt,
+ IN PLARGE_INTEGER PDrainTime
+ );
+
+VOID
+SerialSaveDeviceState(
+ IN PSERIAL_DEVICE_EXTENSION PDevExt
+ );
+
+NTSTATUS
+SerialSetPowerPolicy(
+ IN PSERIAL_DEVICE_EXTENSION DeviceExtension
+ );
+
+UINT32
+SerialReportMaxBaudRate(
+ ULONG Bauds
+ );
+
+BOOLEAN
+SerialInsertQueueDpc(
+ IN WDFDPC Dpc
+ );
+
+BOOLEAN
+SerialSetTimer(
+ IN WDFTIMER Timer,
+ IN LARGE_INTEGER DueTime
+ );
+
+BOOLEAN
+SerialCancelTimer(
+ IN WDFTIMER Timer,
+ IN PSERIAL_DEVICE_EXTENSION PDevExt
+ );
+
+VOID
+SerialUnlockPages(
+ IN WDFDPC PDpc,
+ IN PVOID PDeferredContext,
+ IN PVOID PSysContext1,
+ IN PVOID PSysContext2)
+ ;
+
+VOID
+SerialMarkHardwareBroken(
+ IN PSERIAL_DEVICE_EXTENSION PDevExt
+ );
+
+VOID
+SerialDisableInterfacesResources(
+ IN PSERIAL_DEVICE_EXTENSION PDevExt,
+ IN BOOLEAN DisableUART
+ );
+
+VOID
+SerialSetDeviceFlags(
+ IN PSERIAL_DEVICE_EXTENSION PDevExt,
+ OUT PULONG PFlags,
+ IN ULONG Value,
+ IN BOOLEAN Set
+ );
+
+
+VOID
+SetDeviceIsOpened(
+ IN PSERIAL_DEVICE_EXTENSION PDevExt,
+ IN BOOLEAN DeviceIsOpened,
+ IN BOOLEAN Reopen
+ );
+
+BOOLEAN
+IsQueueEmpty(
+ IN WDFQUEUE Queue
+ );
+
+NTSTATUS
+SerialCreateTimersAndDpcs(
+ IN PSERIAL_DEVICE_EXTENSION PDevExt
+ );
+
+VOID
+SerialDrainTimersAndDpcs(
+ IN PSERIAL_DEVICE_EXTENSION PDevExt
+ );
+
+VOID
+SerialSetCancelRoutine(
+ IN WDFREQUEST Request,
+ IN PFN_WDF_REQUEST_CANCEL CancelRoutine
+ );
+
+NTSTATUS
+SerialClearCancelRoutine(
+ IN WDFREQUEST Request,
+ IN BOOLEAN ClearReference
+ );
+
+NTSTATUS
+SerialWmiRegistration(
+ WDFDEVICE Device
+ );
+
+NTSTATUS
+SerialReadSymName(
+ IN WDFDEVICE Device,
+ _Out_writes_bytes_(*SizeOfRegName) PWSTR RegName,
+ _Inout_ PUSHORT SizeOfRegName
+ );
+
+VOID
+SerialCompleteRequest(
+ IN WDFREQUEST Request,
+ IN NTSTATUS Status,
+ IN ULONG_PTR Info
+ );
+
+BOOLEAN
+SerialGetFdoRegistryKeyValue(
+ IN PWDFDEVICE_INIT DeviceInit,
+ _In_ PCWSTR Name,
+ OUT PULONG Value
+ );
+
+VOID
+SerialSetInterruptPolicy(
+ _In_ WDFINTERRUPT WdfInterrupt
+ );
+
+typedef struct _SERIAL_UPDATE_CHAR {
+ PSERIAL_DEVICE_EXTENSION Extension;
+ ULONG CharsCopied;
+ BOOLEAN Completed;
+ } SERIAL_UPDATE_CHAR,*PSERIAL_UPDATE_CHAR;
+
+//
+// The following simple structure is used to send a pointer
+// the device extension and an ioctl specific pointer
+// to data.
+//
+typedef struct _SERIAL_IOCTL_SYNC {
+ PSERIAL_DEVICE_EXTENSION Extension;
+ PVOID Data;
+ } SERIAL_IOCTL_SYNC,*PSERIAL_IOCTL_SYNC;
+
+
+//
+// The following three macros are used to initialize, set
+// and clear references in IRPs that are used by
+// this driver. The reference is stored in the fourth
+// argument of the request, which is never used by any operation
+// accepted by this driver.
+//
+
+#define SERIAL_REF_ISR (0x00000001)
+#define SERIAL_REF_CANCEL (0x00000002)
+#define SERIAL_REF_TOTAL_TIMER (0x00000004)
+#define SERIAL_REF_INT_TIMER (0x00000008)
+#define SERIAL_REF_XOFF_REF (0x00000010)
+
+
+#define SERIAL_INIT_REFERENCE(ReqContext) { \
+ (ReqContext)->RefCount = NULL; \
+ }
+
+#define SERIAL_SET_REFERENCE(ReqContext, RefType) \
+ do { \
+ LONG _refType = (RefType); \
+ PULONG_PTR _arg4 = (PVOID)&(ReqContext)->RefCount; \
+ ASSERT(!(*_arg4 & _refType)); \
+ *_arg4 |= _refType; \
+ } WHILE (0)
+
+#define SERIAL_CLEAR_REFERENCE(ReqContext, RefType) \
+ do { \
+ LONG _refType = (RefType); \
+ PULONG_PTR _arg4 = (PVOID)&(ReqContext)->RefCount; \
+ ASSERT(*_arg4 & _refType); \
+ *_arg4 &= ~_refType; \
+ } WHILE (0)
+
+#define SERIAL_REFERENCE_COUNT(ReqContext) \
+ ((ULONG_PTR)(((ReqContext)->RefCount)))
+
+#define SERIAL_TEST_REFERENCE(ReqContext, RefType) ((ULONG_PTR)ReqContext ->RefCount & RefType)
+
+//
+// Prototypes and defines to handle processor groups.
+//
+typedef
+USHORT
+(*PFN_KE_GET_ACTIVE_GROUP_COUNT)(
+ VOID
+ );
+
+typedef
+KAFFINITY
+(*PFN_KE_QUERY_GROUP_AFFINITY) (
+ _In_ USHORT GroupNumber
+ );
+
+//
+// Force the serial interrupt to run on the last interrupt group.
+//
+//#define SERIAL_SELECT_INTERRUPT_GROUP 1
+#define SERIAL_LAST_INTERRUPT_GROUP 0xFFFF
+#define SERIAL_PREFERRED_INTERRUPT_GROUP SERIAL_LAST_INTERRUPT_GROUP
+
+
+
diff --git a/serial/serial/serlog.mc b/serial/serial/serlog.mc
new file mode 100644
index 00000000..ee6935b6
--- /dev/null
+++ b/serial/serial/serlog.mc
@@ -0,0 +1,290 @@
+;/*++ BUILD Version: 0001 // Increment this if a change has global effects
+;
+;Copyright (c) 1992, 1993 Microsoft Corporation
+;
+;Module Name:
+;
+; ntiologc.h
+;
+;Abstract:
+;
+; Constant definitions for the I/O error code log values.
+;
+;--*/
+;
+;#ifndef _SERLOG_
+;#define _SERLOG_
+;
+;//
+;// Status values are 32 bit values layed out as follows:
+;//
+;// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
+;// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+;// +---+-+-------------------------+-------------------------------+
+;// |Sev|C| Facility | Code |
+;// +---+-+-------------------------+-------------------------------+
+;//
+;// where
+;//
+;// Sev - is the severity code
+;//
+;// 00 - Success
+;// 01 - Informational
+;// 10 - Warning
+;// 11 - Error
+;//
+;// C - is the Customer code flag
+;//
+;// Facility - is the facility code
+;//
+;// Code - is the facility's status code
+;//
+;
+MessageIdTypedef=NTSTATUS
+
+SeverityNames=(Success=0x0:STATUS_SEVERITY_SUCCESS
+ Informational=0x1:STATUS_SEVERITY_INFORMATIONAL
+ Warning=0x2:STATUS_SEVERITY_WARNING
+ Error=0x3:STATUS_SEVERITY_ERROR
+ )
+
+FacilityNames=(System=0x0
+ RpcRuntime=0x2:FACILITY_RPC_RUNTIME
+ RpcStubs=0x3:FACILITY_RPC_STUBS
+ Io=0x4:FACILITY_IO_ERROR_CODE
+ Serial=0x6:FACILITY_SERIAL_ERROR_CODE
+ )
+
+
+MessageId=0x0001 Facility=Serial Severity=Informational SymbolicName=SERIAL_KERNEL_DEBUGGER_ACTIVE
+Language=English
+The kernel debugger is already using %2.
+.
+
+MessageId=0x0002 Facility=Serial Severity=Informational SymbolicName=SERIAL_FIFO_PRESENT
+Language=English
+While validating that %2 was really a serial port, a fifo was detected. The fifo will be used.
+.
+
+MessageId=0x0003 Facility=Serial Severity=Informational SymbolicName=SERIAL_USER_OVERRIDE
+Language=English
+User configuration data for parameter %2 overriding firmware configuration data.
+.
+
+MessageId=0x0004 Facility=Serial Severity=Warning SymbolicName=SERIAL_NO_SYMLINK_CREATED
+Language=English
+Unable to create the symbolic link for %2.
+.
+
+MessageId=0x0005 Facility=Serial Severity=Warning SymbolicName=SERIAL_NO_DEVICE_MAP_CREATED
+Language=English
+Unable to create the device map entry for %2.
+.
+
+MessageId=0x0006 Facility=Serial Severity=Warning SymbolicName=SERIAL_NO_DEVICE_MAP_DELETED
+Language=English
+Unable to delete the device map entry for %2.
+.
+
+MessageId=0x0007 Facility=Serial Severity=Error SymbolicName=SERIAL_UNREPORTED_IRQL_CONFLICT
+Language=English
+Another driver on the system, which did not report its resources, has already claimed the interrupt used by %2.
+.
+
+MessageId=0x0008 Facility=Serial Severity=Error SymbolicName=SERIAL_INSUFFICIENT_RESOURCES
+Language=English
+Not enough resources were available for the driver.
+.
+
+MessageId=0x0009 Facility=Serial Severity=Error SymbolicName=SERIAL_UNSUPPORTED_CLOCK_RATE
+Language=English
+The baud clock rate configuration is not supported on device %2.
+.
+
+MessageId=0x000A Facility=Serial Severity=Error SymbolicName=SERIAL_REGISTERS_NOT_MAPPED
+Language=English
+The hardware locations for %2 could not be translated to something the memory management system could understand.
+.
+
+MessageId=0x000B Facility=Serial Severity=Error SymbolicName=SERIAL_RESOURCE_CONFLICT
+Language=English
+The hardware resources for %2 are already in use by another device.
+.
+
+MessageId=0x000C Facility=Serial Severity=Error SymbolicName=SERIAL_NO_BUFFER_ALLOCATED
+Language=English
+No memory could be allocated in which to place new data for %2.
+.
+
+MessageId=0x000D Facility=Serial Severity=Error SymbolicName=SERIAL_IER_INVALID
+Language=English
+While validating that %2 was really a serial port, the interrupt enable register contained enabled bits in a must be zero bitfield.
+The device is assumed not to be a serial port and will be deleted.
+.
+
+MessageId=0x000E Facility=Serial Severity=Error SymbolicName=SERIAL_MCR_INVALID
+Language=English
+While validating that %2 was really a serial port, the modem control register contained enabled bits in a must be zero bitfield.
+The device is assumed not to be a serial port and will be deleted.
+.
+
+MessageId=0x000F Facility=Serial Severity=Error SymbolicName=SERIAL_IIR_INVALID
+Language=English
+While validating that %2 was really a serial port, the interrupt id register contained enabled bits in a must be zero bitfield.
+The device is assumed not to be a serial port and will be deleted.
+.
+
+MessageId=0x0010 Facility=Serial Severity=Error SymbolicName=SERIAL_DL_INVALID
+Language=English
+While validating that %2 was really a serial port, the baud rate register could not be set consistantly.
+The device is assumed not to be a serial port and will be deleted.
+.
+
+MessageId=0x0011 Facility=Serial Severity=Error SymbolicName=SERIAL_NOT_ENOUGH_CONFIG_INFO
+Language=English
+Some firmware configuration information was incomplete.
+.
+
+MessageId=0x0012 Facility=Serial Severity=Error SymbolicName=SERIAL_NO_PARAMETERS_INFO
+Language=English
+No Parameters subkey was found for user defined data. This is odd, and it also means no user configuration can be found.
+.
+
+MessageId=0x0013 Facility=Serial Severity=Error SymbolicName=SERIAL_UNABLE_TO_ACCESS_CONFIG
+Language=English
+Specific user configuration data is unretrievable.
+.
+
+MessageId=0x0014 Facility=Serial Severity=Error SymbolicName=SERIAL_INVALID_PORT_INDEX
+Language=English
+On parameter %2 which indicates a multiport card, must have a port index specified greater than 0.
+.
+
+MessageId=0x0015 Facility=Serial Severity=Error SymbolicName=SERIAL_PORT_INDEX_TOO_HIGH
+Language=English
+On parameter %2 which indicates a multiport card, the port index for the multiport card is too large.
+.
+
+MessageId=0x0016 Facility=Serial Severity=Error SymbolicName=SERIAL_UNKNOWN_BUS
+Language=English
+The bus type for %2 is not recognizable.
+.
+
+MessageId=0x0017 Facility=Serial Severity=Error SymbolicName=SERIAL_BUS_NOT_PRESENT
+Language=English
+The bus type for %2 is not available on this computer.
+.
+
+MessageId=0x0018 Facility=Serial Severity=Error SymbolicName=SERIAL_BUS_INTERRUPT_CONFLICT
+Language=English
+The bus specified for %2 does not support the specified method of interrupt.
+.
+
+MessageId=0x0019 Facility=Serial Severity=Error SymbolicName=SERIAL_INVALID_USER_CONFIG
+Language=English
+User configuration for parameter %2 must have %3.
+.
+
+MessageId=0x001A Facility=Serial Severity=Error SymbolicName=SERIAL_DEVICE_TOO_HIGH
+Language=English
+The user specified port for %2 is way too high in physical memory.
+.
+
+MessageId=0x001B Facility=Serial Severity=Error SymbolicName=SERIAL_STATUS_TOO_HIGH
+Language=English
+The status port for %2 is way too high in physical memory.
+.
+
+MessageId=0x001C Facility=Serial Severity=Error SymbolicName=SERIAL_STATUS_CONTROL_CONFLICT
+Language=English
+The status port for %2 overlaps the control registers for the device.
+.
+
+MessageId=0x001D Facility=Serial Severity=Error SymbolicName=SERIAL_CONTROL_OVERLAP
+Language=English
+The control registers for %2 overlaps with the %3 control registers.
+.
+
+MessageId=0x001E Facility=Serial Severity=Error SymbolicName=SERIAL_STATUS_OVERLAP
+Language=English
+The status register for %2 overlaps the %3 control registers.
+.
+
+MessageId=0x001F Facility=Serial Severity=Error SymbolicName=SERIAL_STATUS_STATUS_OVERLAP
+Language=English
+The status register for %2 overlaps with the %3 status register.
+.
+
+MessageId=0x0020 Facility=Serial Severity=Error SymbolicName=SERIAL_CONTROL_STATUS_OVERLAP
+Language=English
+The control registers for %2 overlaps the %3 status register.
+.
+
+MessageId=0x0021 Facility=Serial Severity=Error SymbolicName=SERIAL_MULTI_INTERRUPT_CONFLICT
+Language=English
+Two ports, %2 and %3, on a single multiport card can't have two different interrupts.
+.
+
+MessageId=0x0022 Facility=Serial Severity=Informational SymbolicName=SERIAL_DISABLED_PORT
+Language=English
+Disabling %2 as requested by the configuration data.
+.
+
+MessageId=0x0023 Facility=Serial Severity=Error SymbolicName=SERIAL_GARBLED_PARAMETER
+Language=English
+Parameter %2 data is unretrievable from the registry.
+.
+
+MessageId=0x0024 Facility=Serial Severity=Error SymbolicName=SERIAL_DLAB_INVALID
+Language=English
+While validating that %2 was really a serial port, the contents of the divisor latch register was identical to the interrupt enable and the receive registers.
+The device is assumed not to be a serial port and will be deleted.
+.
+
+MessageId=0x0025 Facility=Serial Severity=Error SymbolicName=SERIAL_NO_TRANSLATE_PORT
+Language=English
+Could not translate the user reported I/O port for %2.
+.
+
+MessageId=0x0026 Facility=Serial Severity=Error SymbolicName=SERIAL_NO_GET_INTERRUPT
+Language=English
+Could not get the user reported interrupt for %2 from the HAL.
+.
+
+MessageId=0x0027 Facility=Serial Severity=Error SymbolicName=SERIAL_NO_TRANSLATE_ISR
+Language=English
+Could not translate the user reported Interrupt Status Register for %2.
+.
+
+MessageId=0x0028 Facility=Serial Severity=Error SymbolicName=SERIAL_NO_DEVICE_REPORT
+Language=English
+Could not report the discovered legacy device %2 to the IO subsystem.
+.
+
+MessageId=0x0029 Facility=Serial Severity=Error SymbolicName=SERIAL_REGISTRY_WRITE_FAILED
+Language=English
+Error writing to the registry.
+.
+
+MessageId=0x002A Facility=Serial Severity=Warning SymbolicName=SERIAL_MOUSE_CONFLICT_IRQ
+Language=English
+There is a serial mouse using the same interrupt as %2. Therefore, %2 will not be started.
+.
+
+MessageId=0x002B Facility=Serial Severity=Warning SymbolicName=SERIAL_MOUSE_ON_PORT
+Language=English
+There was a serial mouse found on %2. Therefore, %2 will be assigned to the mouse.
+.
+
+MessageId=0x002C Facility=Serial Severity=Error SymbolicName=SERIAL_NO_DEVICE_REPORT_RES
+Language=English
+Could not report device %2 to IO subsystem due to a resource conflict.
+.
+
+MessageId=0x002D Facility=Serial Severity=Error SymbolicName=SERIAL_HARDWARE_FAILURE
+Language=English
+The serial driver detected a hardware failure on device %2 and will disable this device.
+.
+
+;#endif /* _NTIOLOGC_ */
+
diff --git a/serial/serial/trace.h b/serial/serial/trace.h
new file mode 100644
index 00000000..f06efa9f
--- /dev/null
+++ b/serial/serial/trace.h
@@ -0,0 +1,118 @@
+/*++
+
+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 <evntrace.h> // For TRACE_LEVEL definitions
+
+#if !defined(EVENT_TRACING)
+
+//
+// TODO: These defines are missing in evntrace.h
+// in some DDK build environments (XP).
+//
+#if !defined(TRACE_LEVEL_NONE)
+ #define TRACE_LEVEL_NONE 0
+ #define TRACE_LEVEL_CRITICAL 1
+ #define TRACE_LEVEL_FATAL 1
+ #define TRACE_LEVEL_ERROR 2
+ #define TRACE_LEVEL_WARNING 3
+ #define TRACE_LEVEL_INFORMATION 4
+ #define TRACE_LEVEL_VERBOSE 5
+ #define TRACE_LEVEL_RESERVED6 6
+ #define TRACE_LEVEL_RESERVED7 7
+ #define TRACE_LEVEL_RESERVED8 8
+ #define TRACE_LEVEL_RESERVED9 9
+#endif
+
+
+//
+// Define Debug Flags
+//
+#define DBG_INIT 0x00000001
+#define DBG_PNP 0x00000002
+#define DBG_POWER 0x00000004
+#define DBG_WMI 0x00000008
+#define DBG_CREATE_CLOSE 0x00000010
+#define DBG_IOCTLS 0x00000020
+#define DBG_WRITE 0x00000040
+#define DBG_READ 0x00000080
+#define DBG_DPC 0x00000100
+#define DBG_INTERRUPT 0x00000200
+#define DBG_LOCKS 0x00000400
+#define DBG_QUEUEING 0x00000800
+#define DBG_HW_ACCESS 0x00001000
+
+VOID
+TraceEvents (
+ IN ULONG DebugPrintLevel,
+ IN ULONG DebugPrintFlag,
+ IN PCCHAR DebugMessage,
+ ...
+ );
+
+#define WPP_INIT_TRACING(DriverObject, RegistryPath)
+#define WPP_CLEANUP(DriverObject)
+
+#else
+//
+// If software tracing is defined in the sources file..
+// WPP_DEFINE_CONTROL_GUID specifies the GUID used for this driver.
+// *** REPLACE THE GUID WITH YOUR OWN UNIQUE ID ***
+// WPP_DEFINE_BIT allows setting debug bit masks to selectively print.
+// The names defined in the WPP_DEFINE_BIT call define the actual names
+// that are used to control the level of tracing for the control guid
+// specified.
+//
+// Name of the logger is Serial and the guid is
+// {F3A79AB6-9827-4419-9465-45CF949EF659}
+// (0xf3a79ab6, 0x9827, 0x4419, 0x94, 0x65, 0x45, 0xcf, 0x94, 0x9e, 0xf6, 0x59);
+//
+
+#define WPP_CHECK_FOR_NULL_STRING //to prevent exceptions due to NULL strings
+
+#define WPP_CONTROL_GUIDS \
+ WPP_DEFINE_CONTROL_GUID(SerialTraceGuid,(bc6c9364,fc67,42c5,acf7,abed3b12ecc6), \
+ WPP_DEFINE_BIT(DBG_INIT) /* bit 0 = 0x00000001 */ \
+ WPP_DEFINE_BIT(DBG_PNP) /* bit 1 = 0x00000002 */ \
+ WPP_DEFINE_BIT(DBG_POWER) /* bit 2 = 0x00000004 */ \
+ WPP_DEFINE_BIT(DBG_WMI) /* bit 3 = 0x00000008 */ \
+ WPP_DEFINE_BIT(DBG_CREATE_CLOSE) /* bit 4 = 0x00000010 */ \
+ WPP_DEFINE_BIT(DBG_IOCTLS) /* bit 5 = 0x00000020 */ \
+ WPP_DEFINE_BIT(DBG_WRITE) /* bit 6 = 0x00000040 */ \
+ WPP_DEFINE_BIT(DBG_READ) /* bit 7 = 0x00000080 */ \
+ WPP_DEFINE_BIT(DBG_DPC) /* bit 8 = 0x00000100 */ \
+ WPP_DEFINE_BIT(DBG_INTERRUPT) /* bit 9 = 0x00000200 */ \
+ WPP_DEFINE_BIT(DBG_LOCKS) /* bit 10 = 0x00000400 */ \
+ WPP_DEFINE_BIT(DBG_QUEUEING) /* bit 11 = 0x00000800 */ \
+ WPP_DEFINE_BIT(DBG_HW_ACCESS) /* bit 12 = 0x00001000 */ \
+ /* You can have up to 32 defines. If you want more than that,\
+ you have to provide another trace control GUID */\
+ )
+
+
+#define WPP_LEVEL_FLAGS_LOGGER(lvl,flags) WPP_LEVEL_LOGGER(flags)
+#define WPP_LEVEL_FLAGS_ENABLED(lvl, flags) (WPP_LEVEL_ENABLED(flags) && WPP_CONTROL(WPP_BIT_ ## flags).Level >= lvl)
+
+
+#endif
+
+
diff --git a/serial/serial/utils.c b/serial/serial/utils.c
new file mode 100644
index 00000000..2386774f
--- /dev/null
+++ b/serial/serial/utils.c
@@ -0,0 +1,1946 @@
+/*++
+
+Copyright (c) Microsoft Corporation
+
+Module Name:
+
+ utils.c
+
+Abstract:
+
+ This module contains code that perform queueing and completion
+ manipulation on requests. Also module generic functions such
+ as error logging.
+
+Environment:
+
+ Kernel mode
+
+--*/
+
+#include "precomp.h"
+
+#if defined(EVENT_TRACING)
+#include "utils.tmh"
+#endif
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGESRP0,SerialMemCompare)
+#pragma alloc_text(PAGESRP0,SerialLogError)
+#pragma alloc_text(PAGESRP0,SerialMarkHardwareBroken)
+#endif // ALLOC_PRAGMA
+
+
+VOID
+SerialRundownIrpRefs(
+ IN WDFREQUEST *CurrentOpRequest,
+ IN WDFTIMER IntervalTimer,
+ IN WDFTIMER TotalTimer,
+ IN PSERIAL_DEVICE_EXTENSION PDevExt,
+ IN LONG RefType
+ );
+
+static const PHYSICAL_ADDRESS SerialPhysicalZero = {0};
+
+VOID
+SerialPurgeRequests(
+ IN WDFQUEUE QueueToClean,
+ IN WDFREQUEST *CurrentOpRequest
+ )
+
+/*++
+
+Routine Description:
+
+ This function is used to cancel all queued and the current irps
+ for reads or for writes. Called at DPC level.
+
+Arguments:
+
+ QueueToClean - A pointer to the queue which we're going to clean out.
+
+ CurrentOpRequest - Pointer to a pointer to the current request.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS status;
+ PREQUEST_CONTEXT reqContext;
+
+ WdfIoQueuePurge(QueueToClean, WDF_NO_EVENT_CALLBACK, WDF_NO_CONTEXT);
+
+ //
+ // The queue is clean. Now go after the current if
+ // it's there.
+ //
+
+ if (*CurrentOpRequest) {
+
+ PFN_WDF_REQUEST_CANCEL CancelRoutine;
+
+ reqContext = SerialGetRequestContext(*CurrentOpRequest);
+ CancelRoutine = reqContext->CancelRoutine;
+ //
+ // Clear the common cancel routine but don't clear the reference because the
+ // request specific cancel routine called below will clear the reference.
+ //
+ status = SerialClearCancelRoutine(*CurrentOpRequest, FALSE);
+ if (NT_SUCCESS(status)) {
+ //
+ // Let us just call the CancelRoutine to start the next request.
+ //
+ if(CancelRoutine) {
+ CancelRoutine(*CurrentOpRequest);
+ }
+ }
+ }
+}
+
+VOID
+SerialFlushRequests(
+ IN WDFQUEUE QueueToClean,
+ IN WDFREQUEST *CurrentOpRequest
+ )
+
+/*++
+
+Routine Description:
+
+ This function is used to cancel all queued and the current irps
+ for reads or for writes. Called at DPC level.
+
+Arguments:
+
+ QueueToClean - A pointer to the queue which we're going to clean out.
+
+ CurrentOpRequest - Pointer to a pointer to the current request.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ SerialPurgeRequests(QueueToClean, CurrentOpRequest);
+
+ //
+ // Since purge puts the queue state to fail requests, we have to explicitly
+ // change the queue state to accept requests.
+ //
+ WdfIoQueueStart(QueueToClean);
+
+}
+
+
+VOID
+SerialGetNextRequest(
+ IN WDFREQUEST * CurrentOpRequest,
+ IN WDFQUEUE QueueToProcess,
+ OUT WDFREQUEST * NextRequest,
+ IN BOOLEAN CompleteCurrent,
+ IN PSERIAL_DEVICE_EXTENSION Extension
+ )
+
+/*++
+
+Routine Description:
+
+ This function is used to make the head of the particular
+ queue the current request. It also completes the what
+ was the old current request if desired.
+
+Arguments:
+
+ CurrentOpRequest - Pointer to a pointer to the currently active
+ request for the particular work list. Note that
+ this item is not actually part of the list.
+
+ QueueToProcess - The list to pull the new item off of.
+
+ NextIrp - The next Request to process. Note that CurrentOpRequest
+ will be set to this value under protection of the
+ cancel spin lock. However, if *NextIrp is NULL when
+ this routine returns, it is not necessaryly true the
+ what is pointed to by CurrentOpRequest will also be NULL.
+ The reason for this is that if the queue is empty
+ when we hold the cancel spin lock, a new request may come
+ in immediately after we release the lock.
+
+ CompleteCurrent - If TRUE then this routine will complete the
+ request pointed to by the pointer argument
+ CurrentOpRequest.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ WDFREQUEST oldRequest = NULL;
+ PREQUEST_CONTEXT reqContext;
+ NTSTATUS status;
+
+ UNREFERENCED_PARAMETER(Extension);
+
+ oldRequest = *CurrentOpRequest;
+ *CurrentOpRequest = NULL;
+
+ //
+ // Check to see if there is a new request to start up.
+ //
+
+ status = WdfIoQueueRetrieveNextRequest(
+ QueueToProcess,
+ CurrentOpRequest
+ );
+
+ if(!NT_SUCCESS(status)) {
+ ASSERTMSG("WdfIoQueueRetrieveNextRequest failed",
+ status == STATUS_NO_MORE_ENTRIES);
+ }
+
+ *NextRequest = *CurrentOpRequest;
+
+ if (CompleteCurrent) {
+
+ if (oldRequest) {
+
+ reqContext = SerialGetRequestContext(oldRequest);
+
+ SerialCompleteRequest(oldRequest,
+ reqContext->Status,
+ reqContext->Information);
+ }
+ }
+}
+
+VOID
+SerialTryToCompleteCurrent(
+ IN PSERIAL_DEVICE_EXTENSION Extension,
+ IN PFN_WDF_INTERRUPT_SYNCHRONIZE SynchRoutine OPTIONAL,
+ IN NTSTATUS StatusToUse,
+ IN WDFREQUEST *CurrentOpRequest,
+ IN WDFQUEUE QueueToProcess OPTIONAL,
+ IN WDFTIMER IntervalTimer OPTIONAL,
+ IN WDFTIMER TotalTimer OPTIONAL,
+ IN PSERIAL_START_ROUTINE Starter OPTIONAL,
+ IN PSERIAL_GET_NEXT_ROUTINE GetNextRequest OPTIONAL,
+ IN LONG RefType
+ )
+
+/*++
+
+Routine Description:
+
+ This routine attempts to remove all of the reasons there are
+ references on the current read/write. If everything can be completed
+ it will complete this read/write and try to start another.
+
+ NOTE: This routine assumes that it is called with the cancel
+ spinlock held.
+
+Arguments:
+
+ Extension - Simply a pointer to the device extension.
+
+ SynchRoutine - A routine that will synchronize with the isr
+ and attempt to remove the knowledge of the
+ current request from the isr. NOTE: This pointer
+ can be null.
+
+ IrqlForRelease - This routine is called with the cancel spinlock held.
+ This is the irql that was current when the cancel
+ spinlock was acquired.
+
+ StatusToUse - The request's status field will be set to this value, if
+ this routine can complete the request.
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PREQUEST_CONTEXT reqContext;
+
+ ASSERTMSG("SerialTryToCompleteCurrent: CurrentOpRequest is NULL", *CurrentOpRequest);
+
+ reqContext = SerialGetRequestContext(*CurrentOpRequest);
+
+ if(RefType == SERIAL_REF_ISR || RefType == SERIAL_REF_XOFF_REF) {
+ //
+ // We can decrement the reference to "remove" the fact
+ // that the caller no longer will be accessing this request.
+ //
+
+ SERIAL_CLEAR_REFERENCE(
+ reqContext,
+ RefType
+ );
+ }
+
+ if (SynchRoutine) {
+
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SynchRoutine,
+ Extension
+ );
+
+ }
+
+ //
+ // Try to run down all other references to this request.
+ //
+
+ SerialRundownIrpRefs(
+ CurrentOpRequest,
+ IntervalTimer,
+ TotalTimer,
+ Extension,
+ RefType
+ );
+
+ if(StatusToUse == STATUS_CANCELLED) {
+ //
+ // This function is called from a cancelroutine. So mark
+ // the request as cancelled. We need to do this because
+ // we may not complete the request below if somebody
+ // else has a reference to it.
+ // This state variable was added to avoid calling
+ // WdfRequestMarkCancelable second time on a request that
+ // has cancelled but wasn't completed in the cancel routine.
+ //
+ reqContext->Cancelled = TRUE;
+ }
+
+ //
+ // See if the ref count is zero after trying to complete everybody else.
+ //
+
+ if (!SERIAL_REFERENCE_COUNT(reqContext)) {
+
+ WDFREQUEST newRequest;
+
+
+ //
+ // The ref count was zero so we should complete this
+ // request.
+ //
+ // The following call will also cause the current request to be
+ // completed.
+ //
+
+ reqContext->Status = StatusToUse;
+
+ if (StatusToUse == STATUS_CANCELLED) {
+
+ reqContext->Information = 0;
+
+ }
+
+ if (GetNextRequest) {
+
+ GetNextRequest(
+ CurrentOpRequest,
+ QueueToProcess,
+ &newRequest,
+ TRUE,
+ Extension
+ );
+
+ if (newRequest) {
+
+ Starter(Extension);
+
+ }
+
+ } else {
+
+ WDFREQUEST oldRequest = *CurrentOpRequest;
+
+ //
+ // There was no get next routine. We will simply complete
+ // the request. We should make sure that we null out the
+ // pointer to the pointer to this request.
+ //
+
+ *CurrentOpRequest = NULL;
+
+ SerialCompleteRequest(oldRequest,
+ reqContext->Status,
+ reqContext->Information);
+ }
+
+ } else {
+
+
+ }
+
+}
+
+
+VOID
+SerialEvtIoStop(
+ IN WDFQUEUE Queue,
+ IN WDFREQUEST Request,
+ IN ULONG ActionFlags
+ )
+/*++
+
+Routine Description:
+
+ This callback is invoked for every request pending in the driver (not queue) -
+ in-flight request. The Action parameter tells us why the callback is invoked -
+ because the device is being stopped, removed or suspended. In this
+ driver, we have told the framework not to stop or remove when there
+ are pending requests, so only reason for this callback is when the system is
+ suspending.
+
+Arguments:
+
+ Queue - Queue the request currently belongs to
+ Request - Request that is currently out of queue and being processed by the driver
+ Action - Reason for this callback
+
+Return Value:
+
+ None. Acknowledge the request so that framework can contiue suspending the
+ device.
+
+--*/
+{
+ PREQUEST_CONTEXT reqContext;
+
+ UNREFERENCED_PARAMETER(Queue);
+
+ reqContext = SerialGetRequestContext(Request);
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_WRITE,
+ "--> SerialEvtIoStop %x %p\n", ActionFlags, Request);
+
+ //
+ // System suspends all the timers before asking the driver to goto
+ // sleep. So let us not worry about cancelling the timers. Also the
+ // framework will disconnect the interrupt before calling our
+ // D0Exit handler so we can be sure that nobody will touch the hardware.
+ // So just acknowledge callback to say that we are okay to stop due to
+ // system suspend. Please note that since we have taken a power reference
+ // we will never idle out when there is an open handle. Also we have told
+ // the framework to not stop for resource rebalancing or remove when there are
+ // open handles, so let us not worry about that either.
+ //
+ if (ActionFlags & WdfRequestStopRequestCancelable) {
+ PFN_WDF_REQUEST_CANCEL cancelRoutine;
+
+ //
+ // Request is in a cancelable state. So unmark cancelable before you
+ // acknowledge. We will mark the request cancelable when we resume.
+ //
+ cancelRoutine = reqContext->CancelRoutine;
+
+ SerialClearCancelRoutine(Request, TRUE);
+
+ //
+ // SerialClearCancelRoutine clears the cancel-routine. So set it back
+ // in the context. We will need that when we resume.
+ //
+ reqContext->CancelRoutine = cancelRoutine;
+
+ reqContext->MarkCancelableOnResume = TRUE;
+
+ ActionFlags &= ~WdfRequestStopRequestCancelable;
+ }
+
+ ASSERT(ActionFlags == WdfRequestStopActionSuspend);
+
+ WdfRequestStopAcknowledge(Request, FALSE); // Don't requeue the request
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_WRITE,
+ "<-- SerialEvtIoStop \n");
+}
+
+VOID
+SerialEvtIoResume(
+ IN WDFQUEUE Queue,
+ IN WDFREQUEST Request
+ )
+/*++
+
+Routine Description:
+
+ This callback is invoked for every request pending in the driver - in-flight
+ request - to notify that the hardware is ready for contiuing the processing
+ of the request.
+
+Arguments:
+
+ Queue - Queue the request currently belongs to
+ Request - Request that is currently out of queue and being processed by the driver
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PREQUEST_CONTEXT reqContext;
+
+ UNREFERENCED_PARAMETER(Queue);
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_WRITE,
+ "--> SerialEvtIoResume %p \n", Request);
+
+ reqContext = SerialGetRequestContext(Request);
+
+ //
+ // If we unmarked cancelable on suspend, let us mark it cancelable again.
+ //
+ if (reqContext->MarkCancelableOnResume) {
+ SerialSetCancelRoutine(Request, reqContext->CancelRoutine);
+ reqContext->MarkCancelableOnResume = FALSE;
+ }
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_WRITE,
+ "<-- SerialEvtIoResume \n");
+}
+
+VOID
+SerialRundownIrpRefs(
+ IN WDFREQUEST *CurrentOpRequest,
+ IN WDFTIMER IntervalTimer OPTIONAL,
+ IN WDFTIMER TotalTimer OPTIONAL,
+ IN PSERIAL_DEVICE_EXTENSION PDevExt,
+ IN LONG RefType
+ )
+
+/*++
+
+Routine Description:
+
+ This routine runs through the various items that *could*
+ have a reference to the current read/write. It try's to remove
+ the reason. If it does succeed in removing the reason it
+ will decrement the reference count on the request.
+
+ NOTE: This routine assumes that it is called with the cancel
+ spin lock held.
+
+Arguments:
+
+ CurrentOpRequest - Pointer to a pointer to current request for the
+ particular operation.
+
+ IntervalTimer - Pointer to the interval timer for the operation.
+ NOTE: This could be null.
+
+ TotalTimer - Pointer to the total timer for the operation.
+ NOTE: This could be null.
+
+ PDevExt - Pointer to device extension
+
+Return Value:
+
+ None.
+
+--*/
+
+
+{
+ PREQUEST_CONTEXT reqContext;
+ WDFREQUEST request = *CurrentOpRequest;
+
+ reqContext = SerialGetRequestContext(request);
+
+ if(RefType == SERIAL_REF_CANCEL) {
+ //
+ // Caller is a cancel routine. So just clear the reference.
+ //
+ SERIAL_CLEAR_REFERENCE( reqContext, SERIAL_REF_CANCEL );
+ reqContext->CancelRoutine = NULL;
+
+ } else {
+ //
+ // Try to clear the cancelable state.
+ //
+ SerialClearCancelRoutine(request, TRUE);
+ }
+ if (IntervalTimer) {
+
+ //
+ // Try to cancel the operations interval timer. If the operation
+ // returns true then the timer did have a reference to the
+ // request. Since we've canceled this timer that reference is
+ // no longer valid and we can decrement the reference count.
+ //
+ // If the cancel returns false then this means either of two things:
+ //
+ // a) The timer has already fired.
+ //
+ // b) There never was an interval timer.
+ //
+ // In the case of "b" there is no need to decrement the reference
+ // count since the "timer" never had a reference to it.
+ //
+ // In the case of "a", then the timer itself will be coming
+ // along and decrement it's reference. Note that the caller
+ // of this routine might actually be the this timer, so
+ // decrement the reference.
+ //
+
+ if (SerialCancelTimer(IntervalTimer, PDevExt)) {
+
+ SERIAL_CLEAR_REFERENCE(
+ reqContext,
+ SERIAL_REF_INT_TIMER
+ );
+
+ } else if(RefType == SERIAL_REF_INT_TIMER) { // caller is the timer
+
+ SERIAL_CLEAR_REFERENCE(
+ reqContext,
+ SERIAL_REF_INT_TIMER
+ );
+ }
+
+ }
+
+ if (TotalTimer) {
+
+ //
+ // Try to cancel the operations total timer. If the operation
+ // returns true then the timer did have a reference to the
+ // request. Since we've canceled this timer that reference is
+ // no longer valid and we can decrement the reference count.
+ //
+ // If the cancel returns false then this means either of two things:
+ //
+ // a) The timer has already fired.
+ //
+ // b) There never was an total timer.
+ //
+ // In the case of "b" there is no need to decrement the reference
+ // count since the "timer" never had a reference to it.
+ //
+ // In the case of "a", then the timer itself will be coming
+ // along and decrement it's reference. Note that the caller
+ // of this routine might actually be the this timer, so
+ // decrement the reference.
+ //
+
+ if (SerialCancelTimer(TotalTimer, PDevExt)) {
+
+ SERIAL_CLEAR_REFERENCE(
+ reqContext,
+ SERIAL_REF_TOTAL_TIMER
+ );
+
+ } else if(RefType == SERIAL_REF_TOTAL_TIMER) { // caller is the timer
+
+ SERIAL_CLEAR_REFERENCE(
+ reqContext,
+ SERIAL_REF_TOTAL_TIMER
+ );
+ }
+ }
+}
+
+
+VOID
+SerialStartOrQueue(
+ IN PSERIAL_DEVICE_EXTENSION Extension,
+ IN WDFREQUEST Request,
+ IN WDFQUEUE QueueToExamine,
+ IN WDFREQUEST *CurrentOpRequest,
+ IN PSERIAL_START_ROUTINE Starter
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to either start or queue any requst
+ that can be queued in the driver.
+
+Arguments:
+
+ Extension - Points to the serial device extension.
+
+ Request - The request to either queue or start. In either
+ case the request will be marked pending.
+
+ QueueToExamine - The queue the request will be place on if there
+ is already an operation in progress.
+
+ CurrentOpRequest - Pointer to a pointer to the request the is current
+ for the queue. The pointer pointed to will be
+ set with to Request if what CurrentOpRequest points to
+ is NULL.
+
+ Starter - The routine to call if the queue is empty.
+
+Return Value:
+
+
+--*/
+
+{
+
+ NTSTATUS status;
+ PREQUEST_CONTEXT reqContext;
+ WDF_REQUEST_PARAMETERS params;
+
+ reqContext = SerialGetRequestContext(Request);
+
+ WDF_REQUEST_PARAMETERS_INIT(&params);
+
+ WdfRequestGetParameters(
+ Request,
+ &params);
+
+ //
+ // If this is a write request then take the amount of characters
+ // to write and add it to the count of characters to write.
+ //
+
+ if (params.Type == WdfRequestTypeWrite) {
+
+ Extension->TotalCharsQueued += reqContext->Length;
+
+ } else if ((params.Type == WdfRequestTypeDeviceControl) &&
+ ((params.Parameters.DeviceIoControl.IoControlCode == IOCTL_SERIAL_IMMEDIATE_CHAR) ||
+ (params.Parameters.DeviceIoControl.IoControlCode == IOCTL_SERIAL_XOFF_COUNTER))) {
+
+ reqContext->IoctlCode = params.Parameters.DeviceIoControl.IoControlCode; // We need this in the destroy callback
+
+ Extension->TotalCharsQueued++;
+
+ }
+
+ if (IsQueueEmpty(QueueToExamine) && !(*CurrentOpRequest)) {
+
+ //
+ // There were no current operation. Mark this one as
+ // current and start it up.
+ //
+
+ *CurrentOpRequest = Request;
+
+ Starter(Extension);
+
+ return;
+
+ } else {
+
+ //
+ // We don't know how long the request will be in the
+ // queue. If it gets cancelled while waiting in the queue, we will
+ // be notified by EvtCanceledOnQueue callback so that we can readjust
+ // the lenght or free the buffer.
+ //
+ reqContext->Extension = Extension; // We need this in the destroy callback
+
+ status = WdfRequestForwardToIoQueue(Request, QueueToExamine);
+ if(!NT_SUCCESS(status)) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_READ, "WdfRequestForwardToIoQueue failed%X\n", status);
+ ASSERTMSG("WdfRequestForwardToIoQueue failed ", FALSE);
+ SerialCompleteRequest(Request, status, 0);
+ }
+
+ return;
+ }
+}
+
+VOID
+SerialEvtCanceledOnQueue(
+ IN WDFQUEUE Queue,
+ IN WDFREQUEST Request
+ )
+
+/*++
+
+Routine Description:
+
+ Called when the request is cancelled while it's waiting
+ on the queue. This callback is used instead of EvtCleanupCallback
+ on the request because this one will be called with the
+ presentation lock held.
+
+
+Arguments:
+
+ Queue - Queue in which the request currently waiting
+ Request - Request being cancelled
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PSERIAL_DEVICE_EXTENSION extension = NULL;
+ PREQUEST_CONTEXT reqContext;
+
+ UNREFERENCED_PARAMETER(Queue);
+
+ reqContext = SerialGetRequestContext(Request);
+
+ extension = reqContext->Extension;
+
+ //
+ // If this is a write request then take the amount of characters
+ // to write and subtract it from the count of characters to write.
+ //
+
+ if (reqContext->MajorFunction == IRP_MJ_WRITE) {
+
+ extension->TotalCharsQueued -= reqContext->Length;
+
+ } else if (reqContext->MajorFunction == IRP_MJ_DEVICE_CONTROL) {
+
+ //
+ // If it's an immediate then we need to decrement the
+ // count of chars queued. If it's a resize then we
+ // need to deallocate the pool that we're passing on
+ // to the "resizing" routine.
+ //
+
+ if (( reqContext->IoctlCode == IOCTL_SERIAL_IMMEDIATE_CHAR) ||
+ (reqContext->IoctlCode == IOCTL_SERIAL_XOFF_COUNTER)) {
+
+ extension->TotalCharsQueued--;
+
+ } else if (reqContext->IoctlCode == IOCTL_SERIAL_SET_QUEUE_SIZE) {
+
+ //
+ // We shoved the pointer to the memory into the
+ // the type 3 buffer pointer which we KNOW we
+ // never use.
+ //
+
+ ASSERT(reqContext->Type3InputBuffer);
+
+ ExFreePool(reqContext->Type3InputBuffer);
+
+ reqContext->Type3InputBuffer = NULL;
+
+ }
+
+ }
+
+ SerialCompleteRequest(Request, WdfRequestGetStatus(Request), 0);
+}
+
+
+NTSTATUS
+SerialCompleteIfError(
+ PSERIAL_DEVICE_EXTENSION extension,
+ WDFREQUEST Request
+ )
+
+/*++
+
+Routine Description:
+
+ If the current request is not an IOCTL_SERIAL_GET_COMMSTATUS request and
+ there is an error and the application requested abort on errors,
+ then cancel the request.
+
+Arguments:
+
+ extension - Pointer to the device context
+
+ Request - Pointer to the WDFREQUEST to test.
+
+Return Value:
+
+ STATUS_SUCCESS or STATUS_CANCELLED.
+
+--*/
+
+{
+
+ WDF_REQUEST_PARAMETERS params;
+ NTSTATUS status = STATUS_SUCCESS;
+
+ if ((extension->HandFlow.ControlHandShake &
+ SERIAL_ERROR_ABORT) && extension->ErrorWord) {
+
+ WDF_REQUEST_PARAMETERS_INIT(&params);
+
+ WdfRequestGetParameters(
+ Request,
+ &params
+ );
+
+
+ //
+ // There is a current error in the driver. No requests should
+ // come through except for the GET_COMMSTATUS.
+ //
+
+ if ((params.Type != WdfRequestTypeDeviceControl) ||
+ (params.Parameters.DeviceIoControl.IoControlCode != IOCTL_SERIAL_GET_COMMSTATUS)) {
+ status = STATUS_CANCELLED;
+ SerialCompleteRequest(Request, status, 0);
+ }
+
+ }
+
+ return status;
+
+}
+
+NTSTATUS
+SerialCreateTimersAndDpcs(
+ IN PSERIAL_DEVICE_EXTENSION pDevExt
+ )
+/*++
+
+Routine Description:
+
+ This function creates all the timers and DPC objects. All the objects
+ are associated with the WDFDEVICE and the callbacks are serialized
+ with the device callbacks. Also these objects will be deleted automatically
+ when the device is deleted, so there is no need for the driver to explicitly
+ delete the objects.
+
+Arguments:
+
+ PDevExt - Pointer to the device extension for the device
+
+Return Value:
+
+ return NTSTATUS
+
+--*/
+{
+ WDF_DPC_CONFIG dpcConfig;
+ WDF_TIMER_CONFIG timerConfig;
+ NTSTATUS status;
+ WDF_OBJECT_ATTRIBUTES dpcAttributes;
+ WDF_OBJECT_ATTRIBUTES timerAttributes;
+
+ //
+ // Initialize all the timers used to timeout operations.
+ //
+ //
+ // This timer dpc is fired off if the timer for the total timeout
+ // for the read expires. It will cause the current read to complete.
+ //
+
+ WDF_TIMER_CONFIG_INIT(&timerConfig, SerialReadTimeout);
+
+ timerConfig.AutomaticSerialization = TRUE;
+
+ WDF_OBJECT_ATTRIBUTES_INIT(&timerAttributes);
+ timerAttributes.ParentObject = pDevExt->WdfDevice;
+
+ status = WdfTimerCreate(&timerConfig,
+ &timerAttributes,
+ &pDevExt->ReadRequestTotalTimer);
+
+ if (!NT_SUCCESS(status)) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, "WdfTimerCreate(ReadRequestTotalTimer) failed [%#08lx]\n", status);
+ return status;
+ }
+
+ //
+ // This dpc is fired off if the timer for the interval timeout
+ // expires. If no more characters have been read then the
+ // dpc routine will cause the read to complete. However, if
+ // more characters have been read then the dpc routine will
+ // resubmit the timer.
+ //
+ WDF_TIMER_CONFIG_INIT(&timerConfig, SerialIntervalReadTimeout);
+
+ timerConfig.AutomaticSerialization = TRUE;
+
+ WDF_OBJECT_ATTRIBUTES_INIT(&timerAttributes);
+ timerAttributes.ParentObject = pDevExt->WdfDevice;
+
+ status = WdfTimerCreate(&timerConfig,
+ &timerAttributes,
+ &pDevExt->ReadRequestIntervalTimer);
+
+ if (!NT_SUCCESS(status)) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, "WdfTimerCreate(ReadRequestIntervalTimer) failed [%#08lx]\n", status);
+ return status;
+ }
+
+ //
+ // This dpc is fired off if the timer for the total timeout
+ // for the write expires. It will queue a dpc routine that
+ // will cause the current write to complete.
+ //
+ //
+
+ WDF_TIMER_CONFIG_INIT(&timerConfig, SerialWriteTimeout);
+
+ timerConfig.AutomaticSerialization = TRUE;
+
+ WDF_OBJECT_ATTRIBUTES_INIT(&timerAttributes);
+ timerAttributes.ParentObject = pDevExt->WdfDevice;
+
+ status = WdfTimerCreate(&timerConfig,
+ &timerAttributes,
+ &pDevExt->WriteRequestTotalTimer);
+
+ if (!NT_SUCCESS(status)) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, "WdfTimerCreate(WriteRequestTotalTimer) failed [%#08lx]\n", status);
+ return status;
+ }
+
+ //
+ // This dpc is fired off if the transmit immediate char
+ // character times out. The dpc routine will "grab" the
+ // request from the isr and time it out.
+ //
+ WDF_TIMER_CONFIG_INIT(&timerConfig, SerialTimeoutImmediate);
+
+ timerConfig.AutomaticSerialization = TRUE;
+
+ WDF_OBJECT_ATTRIBUTES_INIT(&timerAttributes);
+ timerAttributes.ParentObject = pDevExt->WdfDevice;
+
+ status = WdfTimerCreate(&timerConfig,
+ &timerAttributes,
+ &pDevExt->ImmediateTotalTimer);
+
+ if (!NT_SUCCESS(status)) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, "WdfTimerCreate(ImmediateTotalTimer) failed [%#08lx]\n", status);
+ return status;
+ }
+
+ //
+ // This dpc is fired off if the timer used to "timeout" counting
+ // the number of characters received after the Xoff ioctl is started
+ // expired.
+ //
+
+ WDF_TIMER_CONFIG_INIT(&timerConfig, SerialTimeoutXoff);
+
+ timerConfig.AutomaticSerialization = TRUE;
+
+ WDF_OBJECT_ATTRIBUTES_INIT(&timerAttributes);
+ timerAttributes.ParentObject = pDevExt->WdfDevice;
+
+ status = WdfTimerCreate(&timerConfig,
+ &timerAttributes,
+ &pDevExt->XoffCountTimer);
+
+ if (!NT_SUCCESS(status)) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, "WdfTimerCreate(XoffCountTimer) failed [%#08lx]\n", status);
+ return status;
+ }
+
+ //
+ // This dpc is fired off when a timer expires (after one
+ // character time), so that code can be invoked that will
+ // check to see if we should lower the RTS line when
+ // doing transmit toggling.
+ //
+ WDF_TIMER_CONFIG_INIT(&timerConfig, SerialInvokePerhapsLowerRTS);
+
+ timerConfig.AutomaticSerialization = TRUE;
+
+ WDF_OBJECT_ATTRIBUTES_INIT(&timerAttributes);
+ timerAttributes.ParentObject = pDevExt->WdfDevice;
+
+ status = WdfTimerCreate(&timerConfig,
+ &timerAttributes,
+ &pDevExt->LowerRTSTimer);
+ if (!NT_SUCCESS(status)) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, "WdfTimerCreate(LowerRTSTimer) failed [%#08lx]\n", status);
+ return status;
+ }
+
+ //
+ // Create a DPC to complete read requests.
+ //
+
+ WDF_DPC_CONFIG_INIT(&dpcConfig, SerialCompleteWrite);
+
+ dpcConfig.AutomaticSerialization = TRUE;
+
+ WDF_OBJECT_ATTRIBUTES_INIT(&dpcAttributes);
+ dpcAttributes.ParentObject = pDevExt->WdfDevice;
+
+ status = WdfDpcCreate(&dpcConfig,
+ &dpcAttributes,
+ &pDevExt->CompleteWriteDpc);
+ if (!NT_SUCCESS(status)) {
+
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, "WdfDpcCreate(CompleteWriteDpc) failed [%#08lx]\n", status);
+ return status;
+ }
+
+
+ //
+ // Create a DPC to complete read requests.
+ //
+
+ WDF_DPC_CONFIG_INIT(&dpcConfig, SerialCompleteRead);
+
+ dpcConfig.AutomaticSerialization = TRUE;
+
+ WDF_OBJECT_ATTRIBUTES_INIT(&dpcAttributes);
+ dpcAttributes.ParentObject = pDevExt->WdfDevice;
+
+ status = WdfDpcCreate(&dpcConfig,
+ &dpcAttributes,
+ &pDevExt->CompleteReadDpc);
+
+ if (!NT_SUCCESS(status)) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, "WdfDpcCreate(CompleteReadDpc) failed [%#08lx]\n", status);
+ return status;
+ }
+
+ //
+ // This dpc is fired off if a comm error occurs. It will
+ // cancel all pending reads and writes.
+ //
+ WDF_DPC_CONFIG_INIT(&dpcConfig, SerialCommError);
+
+ dpcConfig.AutomaticSerialization = TRUE;
+
+ WDF_OBJECT_ATTRIBUTES_INIT(&dpcAttributes);
+ dpcAttributes.ParentObject = pDevExt->WdfDevice;
+
+ status = WdfDpcCreate(&dpcConfig,
+ &dpcAttributes,
+ &pDevExt->CommErrorDpc);
+
+
+ if (!NT_SUCCESS(status)) {
+
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, "WdfDpcCreate(CommErrorDpc) failed [%#08lx]\n", status);
+ return status;
+ }
+
+ //
+ // This dpc is fired off when the transmit immediate char
+ // character is given to the hardware. It will simply complete
+ // the request.
+ //
+
+ WDF_DPC_CONFIG_INIT(&dpcConfig, SerialCompleteImmediate);
+
+ dpcConfig.AutomaticSerialization = TRUE;
+
+ WDF_OBJECT_ATTRIBUTES_INIT(&dpcAttributes);
+ dpcAttributes.ParentObject = pDevExt->WdfDevice;
+
+ status = WdfDpcCreate(&dpcConfig,
+ &dpcAttributes,
+ &pDevExt->CompleteImmediateDpc);
+ if (!NT_SUCCESS(status)) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, "WdfDpcCreate(CompleteImmediateDpc) failed [%#08lx]\n", status);
+ return status;
+ }
+
+ //
+ // This dpc is fired off if an event occurs and there was
+ // a request waiting on that event. A dpc routine will execute
+ // that completes the request.
+ //
+ WDF_DPC_CONFIG_INIT(&dpcConfig, SerialCompleteWait);
+
+ dpcConfig.AutomaticSerialization = TRUE;
+
+ WDF_OBJECT_ATTRIBUTES_INIT(&dpcAttributes);
+ dpcAttributes.ParentObject = pDevExt->WdfDevice;
+
+ status = WdfDpcCreate(&dpcConfig,
+ &dpcAttributes,
+ &pDevExt->CommWaitDpc);
+ if (!NT_SUCCESS(status)) {
+
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, "WdfDpcCreate(CommWaitDpc) failed [%#08lx]\n", status);
+ return status;
+ }
+
+ //
+ // This dpc is fired off if the xoff counter actually runs down
+ // to zero.
+ //
+ WDF_DPC_CONFIG_INIT(&dpcConfig, SerialCompleteXoff);
+
+ dpcConfig.AutomaticSerialization = TRUE;
+
+ WDF_OBJECT_ATTRIBUTES_INIT(&dpcAttributes);
+ dpcAttributes.ParentObject = pDevExt->WdfDevice;
+
+ status = WdfDpcCreate(&dpcConfig,
+ &dpcAttributes,
+ &pDevExt->XoffCountCompleteDpc);
+
+ if (!NT_SUCCESS(status)) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, "WdfDpcCreate(XoffCountCompleteDpc) failed [%#08lx]\n", status);
+ return status;
+ }
+
+
+ //
+ // This dpc is fired off only from device level to start off
+ // a timer that will queue a dpc to check if the RTS line
+ // should be lowered when we are doing transmit toggling.
+ //
+ WDF_DPC_CONFIG_INIT(&dpcConfig, SerialStartTimerLowerRTS);
+
+ dpcConfig.AutomaticSerialization = TRUE;
+
+ WDF_OBJECT_ATTRIBUTES_INIT(&dpcAttributes);
+ dpcAttributes.ParentObject = pDevExt->WdfDevice;
+
+ status = WdfDpcCreate(&dpcConfig,
+ &dpcAttributes,
+ &pDevExt->StartTimerLowerRTSDpc);
+ if (!NT_SUCCESS(status)) {
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, "WdfDpcCreate(StartTimerLowerRTSDpc) failed [%#08lx]\n", status);
+ return status;
+ }
+
+ return status;
+}
+
+
+
+
+BOOLEAN
+SerialInsertQueueDpc(IN WDFDPC PDpc)
+/*++
+
+Routine Description:
+
+ This function must be called to queue DPC's for the serial driver.
+
+Arguments:
+
+ PDpc - Pointer to the Dpc object
+
+Return Value:
+
+ Kicks up return value from KeInsertQueueDpc()
+
+--*/
+{
+ //
+ // If the specified DPC object is not currently in the queue, WdfDpcEnqueue
+ // queues the DPC and returns TRUE.
+ //
+
+ return WdfDpcEnqueue(PDpc);
+}
+
+
+
+BOOLEAN
+SerialSetTimer(IN WDFTIMER Timer, IN LARGE_INTEGER DueTime)
+/*++
+
+Routine Description:
+
+ This function must be called to set timers for the serial driver.
+
+Arguments:
+
+ Timer - pointer to timer dispatcher object
+
+ DueTime - time at which the timer should expire
+
+
+Return Value:
+
+ Kicks up return value from KeSetTimerEx()
+
+--*/
+{
+ BOOLEAN result;
+ //
+ // If the timer object was already in the system timer queue, WdfTimerStart returns TRUE
+ //
+ result = WdfTimerStart(Timer, DueTime.QuadPart);
+
+ return result;
+
+}
+
+
+VOID
+SerialDrainTimersAndDpcs(
+ IN PSERIAL_DEVICE_EXTENSION PDevExt
+ )
+/*++
+
+Routine Description:
+
+ This function cancels all the timers and Dpcs and waits for them
+ to run to completion if they are already fired.
+
+Arguments:
+
+ PDevExt - Pointer to the device extension for the device that needs to
+ set a timer
+
+Return Value:
+
+--*/
+{
+ WdfTimerStop(PDevExt->ReadRequestTotalTimer, TRUE);
+
+ WdfTimerStop(PDevExt->ReadRequestIntervalTimer, TRUE);
+
+ WdfTimerStop(PDevExt->WriteRequestTotalTimer, TRUE);
+
+ WdfTimerStop(PDevExt->ImmediateTotalTimer, TRUE);
+
+ WdfTimerStop(PDevExt->XoffCountTimer, TRUE);
+
+ WdfTimerStop(PDevExt->LowerRTSTimer, TRUE);
+
+ WdfDpcCancel(PDevExt->CompleteWriteDpc, TRUE);
+
+ WdfDpcCancel(PDevExt->CompleteReadDpc, TRUE);
+
+ WdfDpcCancel(PDevExt->CommErrorDpc, TRUE);
+
+ WdfDpcCancel(PDevExt->CompleteImmediateDpc, TRUE);
+
+ WdfDpcCancel(PDevExt->CommWaitDpc, TRUE);
+
+ WdfDpcCancel(PDevExt->XoffCountCompleteDpc, TRUE);
+
+ WdfDpcCancel(PDevExt->StartTimerLowerRTSDpc, TRUE);
+
+ return;
+}
+
+
+
+BOOLEAN
+SerialCancelTimer(
+ IN WDFTIMER Timer,
+ IN PSERIAL_DEVICE_EXTENSION PDevExt
+ )
+/*++
+
+Routine Description:
+
+ This function must be called to cancel timers for the serial driver.
+
+Arguments:
+
+ Timer - pointer to timer dispatcher object
+
+ PDevExt - Pointer to the device extension for the device that needs to
+ set a timer
+
+Return Value:
+
+ True if timer was cancelled
+
+--*/
+{
+ UNREFERENCED_PARAMETER(PDevExt);
+
+ return WdfTimerStop(Timer, FALSE);
+}
+
+SERIAL_MEM_COMPARES
+SerialMemCompare(
+ IN PHYSICAL_ADDRESS A,
+ IN ULONG SpanOfA,
+ IN PHYSICAL_ADDRESS B,
+ IN ULONG SpanOfB
+ )
+/*++
+
+Routine Description:
+
+ Compare two phsical address.
+
+Arguments:
+
+ A - One half of the comparison.
+
+ SpanOfA - In units of bytes, the span of A.
+
+ B - One half of the comparison.
+
+ SpanOfB - In units of bytes, the span of B.
+
+
+Return Value:
+
+ The result of the comparison.
+
+--*/
+{
+ LARGE_INTEGER a;
+ LARGE_INTEGER b;
+
+ LARGE_INTEGER lower;
+ ULONG lowerSpan;
+ LARGE_INTEGER higher;
+
+ PAGED_CODE();
+
+ a = A;
+ b = B;
+
+ if (a.QuadPart == b.QuadPart) {
+
+ return AddressesAreEqual;
+
+ }
+
+ if (a.QuadPart > b.QuadPart) {
+
+ higher = a;
+ lower = b;
+ lowerSpan = SpanOfB;
+
+ } else {
+
+ higher = b;
+ lower = a;
+ lowerSpan = SpanOfA;
+
+ }
+
+ if ((higher.QuadPart - lower.QuadPart) >= lowerSpan) {
+
+ return AddressesAreDisjoint;
+
+ }
+
+ return AddressesOverlap;
+
+}
+
+
+VOID
+SerialLogError(
+ _In_ PDRIVER_OBJECT DriverObject,
+ _In_opt_ PDEVICE_OBJECT DeviceObject,
+ _In_ PHYSICAL_ADDRESS P1,
+ _In_ PHYSICAL_ADDRESS P2,
+ _In_ ULONG SequenceNumber,
+ _In_ UCHAR MajorFunctionCode,
+ _In_ UCHAR RetryCount,
+ _In_ ULONG UniqueErrorValue,
+ _In_ NTSTATUS FinalStatus,
+ _In_ NTSTATUS SpecificIOStatus,
+ _In_ ULONG LengthOfInsert1,
+ _In_reads_bytes_opt_(LengthOfInsert1) PWCHAR Insert1,
+ _In_ ULONG LengthOfInsert2,
+ _In_reads_bytes_opt_(LengthOfInsert2) PWCHAR Insert2
+ )
+/*++
+
+Routine Description:
+
+ This routine allocates an error log entry, copies the supplied data
+ to it, and requests that it be written to the error log file.
+
+Arguments:
+
+ DriverObject - A pointer to the driver object for the device.
+
+ DeviceObject - A pointer to the device object associated with the
+ device that had the error, early in initialization, one may not
+ yet exist.
+
+ P1,P2 - If phyical addresses for the controller ports involved
+ with the error are available, put them through as dump data.
+
+ SequenceNumber - A ulong value that is unique to an WDFREQUEST over the
+ life of the request in this driver - 0 generally means an error not
+ associated with an request.
+
+ MajorFunctionCode - If there is an error associated with the request,
+ this is the major function code of that request.
+
+ RetryCount - The number of times a particular operation has been
+ retried.
+
+ UniqueErrorValue - A unique long word that identifies the particular
+ call to this function.
+
+ FinalStatus - The final status given to the request that was associated
+ with this error. If this log entry is being made during one of
+ the retries this value will be STATUS_SUCCESS.
+
+ SpecificIOStatus - The IO status for a particular error.
+
+ LengthOfInsert1 - The length in bytes (including the terminating NULL)
+ of the first insertion string.
+
+ Insert1 - The first insertion string.
+
+ LengthOfInsert2 - The length in bytes (including the terminating NULL)
+ of the second insertion string. NOTE, there must
+ be a first insertion string for their to be
+ a second insertion string.
+
+ Insert2 - The second insertion string.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PIO_ERROR_LOG_PACKET errorLogEntry;
+
+ PVOID objectToUse;
+ SHORT dumpToAllocate = 0;
+ PUCHAR ptrToFirstInsert;
+ PUCHAR ptrToSecondInsert;
+
+ PAGED_CODE();
+
+ if (Insert1 == NULL) {
+ LengthOfInsert1 = 0;
+ }
+
+ if (Insert2 == NULL) {
+ LengthOfInsert2 = 0;
+ }
+
+
+ if (ARGUMENT_PRESENT(DeviceObject)) {
+
+ objectToUse = DeviceObject;
+
+ } else {
+
+ objectToUse = DriverObject;
+
+ }
+
+ if (SerialMemCompare(
+ P1,
+ (ULONG)1,
+ SerialPhysicalZero,
+ (ULONG)1
+ ) != AddressesAreEqual) {
+
+ dumpToAllocate = (SHORT)sizeof(PHYSICAL_ADDRESS);
+
+ }
+
+ if (SerialMemCompare(
+ P2,
+ (ULONG)1,
+ SerialPhysicalZero,
+ (ULONG)1
+ ) != AddressesAreEqual) {
+
+ dumpToAllocate += (SHORT)sizeof(PHYSICAL_ADDRESS);
+
+ }
+
+ errorLogEntry = IoAllocateErrorLogEntry(
+ objectToUse,
+ (UCHAR)(sizeof(IO_ERROR_LOG_PACKET) +
+ dumpToAllocate
+ + LengthOfInsert1 +
+ LengthOfInsert2)
+ );
+
+ if ( errorLogEntry != NULL ) {
+
+ errorLogEntry->ErrorCode = SpecificIOStatus;
+ errorLogEntry->SequenceNumber = SequenceNumber;
+ errorLogEntry->MajorFunctionCode = MajorFunctionCode;
+ errorLogEntry->RetryCount = RetryCount;
+ errorLogEntry->UniqueErrorValue = UniqueErrorValue;
+ errorLogEntry->FinalStatus = FinalStatus;
+ errorLogEntry->DumpDataSize = dumpToAllocate;
+
+ if (dumpToAllocate) {
+
+ RtlCopyMemory(
+ &errorLogEntry->DumpData[0],
+ &P1,
+ sizeof(PHYSICAL_ADDRESS)
+ );
+
+ if (dumpToAllocate > sizeof(PHYSICAL_ADDRESS)) {
+
+ RtlCopyMemory(
+ ((PUCHAR)&errorLogEntry->DumpData[0])
+ +sizeof(PHYSICAL_ADDRESS),
+ &P2,
+ sizeof(PHYSICAL_ADDRESS)
+ );
+
+ ptrToFirstInsert =
+ ((PUCHAR)&errorLogEntry->DumpData[0])+(2*sizeof(PHYSICAL_ADDRESS));
+
+ } else {
+
+ ptrToFirstInsert =
+ ((PUCHAR)&errorLogEntry->DumpData[0])+sizeof(PHYSICAL_ADDRESS);
+
+
+ }
+
+ } else {
+
+ ptrToFirstInsert = (PUCHAR)&errorLogEntry->DumpData[0];
+
+ }
+
+ ptrToSecondInsert = ptrToFirstInsert + LengthOfInsert1;
+
+ if (LengthOfInsert1) {
+
+ errorLogEntry->NumberOfStrings = 1;
+ errorLogEntry->StringOffset = (USHORT)(ptrToFirstInsert -
+ (PUCHAR)errorLogEntry);
+ RtlCopyMemory(
+ ptrToFirstInsert,
+ Insert1,
+ LengthOfInsert1
+ );
+
+ if (LengthOfInsert2) {
+
+ errorLogEntry->NumberOfStrings = 2;
+ RtlCopyMemory(
+ ptrToSecondInsert,
+ Insert2,
+ LengthOfInsert2
+ );
+
+ }
+
+ }
+
+ IoWriteErrorLogEntry(errorLogEntry);
+
+ }
+
+}
+
+VOID
+SerialMarkHardwareBroken(IN PSERIAL_DEVICE_EXTENSION PDevExt)
+/*++
+
+Routine Description:
+
+ Marks a UART as broken. This causes the driver stack to stop accepting
+ requests and eventually be removed.
+
+Arguments:
+ PDevExt - Device extension attached to PDevObj
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PAGED_CODE();
+
+ //
+ // Write a log entry
+ //
+
+ SerialLogError(PDevExt->DriverObject, NULL, SerialPhysicalZero,
+ SerialPhysicalZero, 0, 0, 0, 88, STATUS_SUCCESS,
+ SERIAL_HARDWARE_FAILURE, PDevExt->DeviceName.Length
+ + sizeof(WCHAR), PDevExt->DeviceName.Buffer, 0, NULL);
+
+ SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_INIT, "Device is broken. Request a restart...\n");
+ WdfDeviceSetFailed(PDevExt->WdfDevice, WdfDeviceFailedAttemptRestart);
+}
+
+NTSTATUS
+SerialGetDivisorFromBaud(
+ IN ULONG ClockRate,
+ IN LONG DesiredBaud,
+ OUT PSHORT AppropriateDivisor
+ )
+
+/*++
+
+Routine Description:
+
+ This routine will determine a divisor based on an unvalidated
+ baud rate.
+
+Arguments:
+
+ ClockRate - The clock input to the controller.
+
+ DesiredBaud - The baud rate for whose divisor we seek.
+
+ AppropriateDivisor - Given that the DesiredBaud is valid, the
+ LONG pointed to by this parameter will be set to the appropriate
+ value. NOTE: The long is undefined if the DesiredBaud is not
+ supported.
+
+Return Value:
+
+ This function will return STATUS_SUCCESS if the baud is supported.
+ If the value is not supported it will return a status such that
+ NT_ERROR(Status) == FALSE.
+
+--*/
+
+{
+
+ NTSTATUS status = STATUS_SUCCESS;
+ SHORT calculatedDivisor;
+ ULONG denominator;
+ ULONG remainder;
+
+ //
+ // Allow up to a 1 percent error
+ //
+
+ ULONG maxRemain18 = 18432;
+ ULONG maxRemain30 = 30720;
+ ULONG maxRemain42 = 42336;
+ ULONG maxRemain80 = 80000;
+ ULONG maxRemain;
+
+
+
+ //
+ // Reject any non-positive bauds.
+ //
+
+ denominator = DesiredBaud*(ULONG)16;
+
+ if (DesiredBaud <= 0) {
+
+ *AppropriateDivisor = -1;
+
+ } else if ((LONG)denominator < DesiredBaud) {
+
+ //
+ // If the desired baud was so huge that it cause the denominator
+ // calculation to wrap, don't support it.
+ //
+
+ *AppropriateDivisor = -1;
+
+ } else {
+
+ if (ClockRate == 1843200) {
+ maxRemain = maxRemain18;
+ } else if (ClockRate == 3072000) {
+ maxRemain = maxRemain30;
+ } else if (ClockRate == 4233600) {
+ maxRemain = maxRemain42;
+ } else {
+ maxRemain = maxRemain80;
+ }
+
+ calculatedDivisor = (SHORT)(ClockRate / denominator);
+ remainder = ClockRate % denominator;
+
+ //
+ // Round up.
+ //
+
+ if (((remainder*2) > ClockRate) && (DesiredBaud != 110)) {
+
+ calculatedDivisor++;
+ }
+
+
+ //
+ // Only let the remainder calculations effect us if
+ // the baud rate is > 9600.
+ //
+
+ if (DesiredBaud >= 9600) {
+
+ //
+ // If the remainder is less than the maximum remainder (wrt
+ // the ClockRate) or the remainder + the maximum remainder is
+ // greater than or equal to the ClockRate then assume that the
+ // baud is ok.
+ //
+
+ if ((remainder >= maxRemain) && ((remainder+maxRemain) < ClockRate)) {
+ calculatedDivisor = -1;
+ }
+
+ }
+
+ //
+ // Don't support a baud that causes the denominator to
+ // be larger than the clock.
+ //
+
+ if (denominator > ClockRate) {
+
+ calculatedDivisor = -1;
+
+ }
+
+ //
+ // Ok, Now do some special casing so that things can actually continue
+ // working on all platforms.
+ //
+
+ if (ClockRate == 1843200) {
+
+ if (DesiredBaud == 56000) {
+ calculatedDivisor = 2;
+ }
+
+ } else if (ClockRate == 3072000) {
+
+ if (DesiredBaud == 14400) {
+ calculatedDivisor = 13;
+ }
+
+ } else if (ClockRate == 4233600) {
+
+ if (DesiredBaud == 9600) {
+ calculatedDivisor = 28;
+ } else if (DesiredBaud == 14400) {
+ calculatedDivisor = 18;
+ } else if (DesiredBaud == 19200) {
+ calculatedDivisor = 14;
+ } else if (DesiredBaud == 38400) {
+ calculatedDivisor = 7;
+ } else if (DesiredBaud == 56000) {
+ calculatedDivisor = 5;
+ }
+
+ } else if (ClockRate == 8000000) {
+
+ if (DesiredBaud == 14400) {
+ calculatedDivisor = 35;
+ } else if (DesiredBaud == 56000) {
+ calculatedDivisor = 9;
+ }
+
+ }
+
+ *AppropriateDivisor = calculatedDivisor;
+
+ }
+
+
+ if (*AppropriateDivisor == -1) {
+
+ status = STATUS_INVALID_PARAMETER;
+
+ }
+
+ return status;
+
+}
+
+
+BOOLEAN
+IsQueueEmpty(
+ IN WDFQUEUE Queue
+ )
+{
+ WDF_IO_QUEUE_STATE queueStatus;
+
+ queueStatus = WdfIoQueueGetState( Queue, NULL, NULL );
+
+ return (WDF_IO_QUEUE_IDLE(queueStatus)) ? TRUE : FALSE;
+}
+
+VOID
+SerialSetCancelRoutine(
+ IN WDFREQUEST Request,
+ IN PFN_WDF_REQUEST_CANCEL CancelRoutine)
+{
+ PREQUEST_CONTEXT reqContext = SerialGetRequestContext(Request);
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS,
+ "-->SerialSetCancelRoutine %p \n", Request);
+
+ WdfRequestMarkCancelable(Request, CancelRoutine);
+ SERIAL_SET_REFERENCE(reqContext, SERIAL_REF_CANCEL);
+ reqContext->CancelRoutine = CancelRoutine;
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS,
+ "<-- SerialSetCancelRoutine \n");
+
+ return;
+}
+
+NTSTATUS
+SerialClearCancelRoutine(
+ IN WDFREQUEST Request,
+ IN BOOLEAN ClearReference
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PREQUEST_CONTEXT reqContext = SerialGetRequestContext(Request);
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS,
+ "-->SerialClearCancelRoutine %p %x\n",
+ Request, ClearReference);
+
+ if(SERIAL_TEST_REFERENCE(reqContext, SERIAL_REF_CANCEL))
+ {
+ status = WdfRequestUnmarkCancelable(Request);
+ if (NT_SUCCESS(status)) {
+
+ reqContext->CancelRoutine = NULL;
+ if(ClearReference) {
+
+ SERIAL_CLEAR_REFERENCE( reqContext, SERIAL_REF_CANCEL );
+
+ }
+ } else {
+ ASSERT(status == STATUS_CANCELLED);
+ }
+ }
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS,
+ "-->SerialClearCancelRoutine %p\n", Request);
+
+ return status;
+}
+
+
+VOID
+SerialCompleteRequest(
+ IN WDFREQUEST Request,
+ IN NTSTATUS Status,
+ IN ULONG_PTR Info
+ )
+{
+ PREQUEST_CONTEXT reqContext;
+
+ reqContext = SerialGetRequestContext(Request);
+
+ ASSERT(reqContext->RefCount == 0);
+
+ SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_PNP,
+ "Complete Request: %p %X 0x%I64x\n",
+ (Request), (Status), (Info));
+
+ WdfRequestCompleteWithInformation((Request), (Status), (Info));
+
+}
+
+
diff --git a/serial/serial/waitmask.c b/serial/serial/waitmask.c
new file mode 100644
index 00000000..b356d5e0
--- /dev/null
+++ b/serial/serial/waitmask.c
@@ -0,0 +1,574 @@
+/*++
+
+Copyright (c) Microsoft Corporation
+
+Module Name:
+
+ waitmask.c
+
+Abstract:
+
+ This module contains the code that is very specific to get/set/wait
+ on event mask operations in the serial driver
+
+Environment:
+
+ Kernel mode
+
+--*/
+
+#include "precomp.h"
+
+#if defined(EVENT_TRACING)
+#include "waitmask.tmh"
+#endif
+
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialGrabWaitFromIsr;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialGiveWaitToIsr;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialFinishOldWait;
+
+
+VOID
+SerialStartMask(
+ IN PSERIAL_DEVICE_EXTENSION Extension
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to process the set mask and wait
+ mask ioctls. Calls to this routine are serialized by
+ placing irps in the list under the protection of the
+ cancel spin lock.
+
+Arguments:
+
+ Extension - A pointer to the serial device extension.
+
+Return Value:
+
+ Will return pending for everything put the first
+ request that we actually process. Even in that
+ case it will return pending unless it can complete
+ it right away.
+
+
+--*/
+
+{
+
+
+ WDFREQUEST NewRequest;
+ PREQUEST_CONTEXT reqContext;
+ WDF_REQUEST_PARAMETERS params;
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS,
+ "In SerialStartMask\n");
+
+ ASSERT(Extension->CurrentMaskRequest);
+
+
+ do {
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS,
+ "STARTMASK - CurrentMaskRequest: %p\n",
+ Extension->CurrentMaskRequest);
+
+ WDF_REQUEST_PARAMETERS_INIT(&params);
+
+ WdfRequestGetParameters(
+ Extension->CurrentMaskRequest,
+ &params
+ );
+
+
+ reqContext = SerialGetRequestContext(Extension->CurrentMaskRequest);
+
+ ASSERT((params.Parameters.DeviceIoControl.IoControlCode ==
+ IOCTL_SERIAL_WAIT_ON_MASK) ||
+ (params.Parameters.DeviceIoControl.IoControlCode ==
+ IOCTL_SERIAL_SET_WAIT_MASK));
+
+ if (params.Parameters.DeviceIoControl.IoControlCode ==
+ IOCTL_SERIAL_SET_WAIT_MASK) {
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS,
+ "SERIAL - %p is a SETMASK request\n",
+ Extension->CurrentMaskRequest);
+
+ //
+ // Complete the old wait if there is one.
+ //
+
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SerialFinishOldWait,
+ Extension
+ );
+
+ //
+ // Any current waits should be on its way to completion
+ // at this point. There certainly shouldn't be any
+ // request mask location.
+ //
+
+ ASSERT(!Extension->IrpMaskLocation);
+
+ reqContext->Status = STATUS_SUCCESS;
+
+ //
+ // The following call will also cause the current
+ // call to be completed.
+ //
+
+ SerialGetNextRequest(
+ &Extension->CurrentMaskRequest,
+ Extension->MaskQueue,
+ &NewRequest,
+ TRUE,
+ Extension
+ );
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS,
+ "Perhaps another mask request was found in "
+ "the queue\n"
+ "------- %p/%p <- values should be the same\n",
+ Extension->CurrentMaskRequest, NewRequest);
+
+
+ } else {
+
+ //
+ // First make sure that we have a non-zero mask.
+ // If the app queues a wait on a zero mask it can't
+ // be statisfied so it makes no sense to start it.
+ //
+
+ if ((!Extension->IsrWaitMask) || (Extension->CurrentWaitRequest)) {
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS,
+ "WaitIrp is invalid\n"
+ "------- IsrWaitMask: %x\n"
+ "------- CurrentWaitRequest: %p\n",
+ Extension->IsrWaitMask,
+ Extension->CurrentWaitRequest);
+
+ reqContext->Status = STATUS_INVALID_PARAMETER;
+
+ SerialGetNextRequest(&Extension->CurrentMaskRequest,
+ Extension->MaskQueue, &NewRequest, TRUE,
+ Extension);
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS,
+ "Perhaps another mask request was found "
+ "in the queue\n"
+ "------- %p/%p <- values should be the same\n",
+ Extension->CurrentMaskRequest,NewRequest);
+
+ } else {
+
+ //
+ // Make the current mask request the current wait request and
+ // get a new current mask request. Note that when we get
+ // the new current mask request we DO NOT complete the
+ // old current mask request (which is now the current wait
+ // request.
+ //
+ // Then under the protection of the cancel spin lock
+ // we check to see if the current wait request needs to
+ // be canceled
+ //
+
+ SERIAL_INIT_REFERENCE(reqContext);
+
+ SerialSetCancelRoutine(Extension->CurrentMaskRequest,
+ SerialCancelWait);
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS,
+ "%p will become the current "
+ "wait request\n",
+ Extension->CurrentMaskRequest);
+ //
+ // There should never be a mask location when
+ // there isn't a current wait request. At this point
+ // there shouldn't be a current wait request also.
+ //
+
+ ASSERT(!Extension->IrpMaskLocation);
+ ASSERT(!Extension->CurrentWaitRequest);
+
+ Extension->CurrentWaitRequest = Extension->CurrentMaskRequest;
+
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SerialGiveWaitToIsr,
+ Extension
+ );
+
+ //
+ // Since it isn't really the mask request anymore,
+ // null out that pointer.
+ //
+ Extension->CurrentMaskRequest = NULL;
+
+ //
+ // This will release the cancel spinlock for us
+ //
+
+ SerialGetNextRequest(&Extension->CurrentMaskRequest,
+ Extension->MaskQueue, &NewRequest,
+ FALSE, Extension);
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS,
+ "Perhaps another mask request was "
+ "found in the queue\n"
+ "------- %p/%p <- values should be the "
+ "same\n", Extension->CurrentMaskRequest,
+ NewRequest);
+ }
+
+ }
+
+ } while (NewRequest);
+
+ return;
+
+}
+
+BOOLEAN
+SerialGrabWaitFromIsr(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine will check to see if the ISR still knows about
+ a wait request by checking to see if the IrpMaskLocation is non-null.
+ If it is then it will zero the Irpmasklocation (which in effect
+ grabs the request away from the isr). This routine is only called
+ buy the cancel code for the wait.
+
+ NOTE: This is called by WdfInterruptSynchronize.
+
+Arguments:
+
+ Context - A pointer to the device extension
+
+Return Value:
+
+ Always FALSE.
+
+--*/
+
+{
+ PSERIAL_DEVICE_EXTENSION Extension = Context;
+
+ PREQUEST_CONTEXT reqContext;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ reqContext = SerialGetRequestContext(Extension->CurrentWaitRequest);
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS,
+ "In SerialGrabWaitFromIsr\n");
+
+ if (Extension->IrpMaskLocation) {
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS,
+ "The isr still owns the request %p, mask "
+ "location is %p\n"
+ "------- and system buffer is %p\n",
+ Extension->CurrentWaitRequest,Extension->IrpMaskLocation,
+ reqContext->SystemBuffer);
+
+ //
+ // The isr still "owns" the request.
+ //
+
+ *Extension->IrpMaskLocation = 0;
+ Extension->IrpMaskLocation = NULL;
+
+ reqContext->Information = sizeof(ULONG);
+
+ //
+ // Since the isr no longer references the request we need to
+ // decrement the reference count.
+ //
+
+ SERIAL_CLEAR_REFERENCE(
+ reqContext,
+ SERIAL_REF_ISR
+ );
+
+ }
+
+ return FALSE;
+}
+
+BOOLEAN
+SerialGiveWaitToIsr(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine simply sets a variable in the device extension
+ so that the isr knows that we have a wait request.
+
+ NOTE: This is called by WdfInterruptSynchronize.
+
+ NOTE: This routine assumes that it is called with the
+ cancel spinlock held.
+
+Arguments:
+
+ Context - Simply a pointer to the device extension.
+
+Return Value:
+
+ Always FALSE.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION Extension = Context;
+
+ PREQUEST_CONTEXT reqContext;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ reqContext = SerialGetRequestContext(Extension->CurrentWaitRequest);
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS,
+ "In SerialGiveWaitToIsr\n");
+ //
+ // There certainly shouldn't be a current mask location at
+ // this point since we have a new current wait request.
+ //
+
+ ASSERT(!Extension->IrpMaskLocation);
+
+ //
+ // The isr may or may not actually reference this request. It
+ // won't if the wait can be satisfied immediately. However,
+ // since it will then go through the normal completion sequence,
+ // we need to have an incremented reference count anyway.
+ //
+
+ SERIAL_SET_REFERENCE(
+ reqContext,
+ SERIAL_REF_ISR
+ );
+
+ if (!Extension->HistoryMask) {
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS,
+ "No events occured prior to the wait call"
+ "\n");
+
+ //
+ // Although this wait might not be for empty transmit
+ // queue, it doesn't hurt anything to set it to false.
+ //
+
+ Extension->EmptiedTransmit = FALSE;
+
+ //
+ // Record where the "completion mask" should be set.
+ //
+
+ Extension->IrpMaskLocation = reqContext->SystemBuffer;
+ SerialDbgPrintEx( TRACE_LEVEL_INFORMATION, DBG_IOCTLS,
+ "The isr owns the request %p, mask location is "
+ "%p\n"
+ "------- and system buffer is %p\n",
+ Extension->CurrentWaitRequest,Extension->IrpMaskLocation,
+ reqContext->SystemBuffer);
+
+ } else {
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS,
+ "%x occurred prior to the wait - starting "
+ "the\n"
+ "------- completion code for %p\n",
+ Extension->HistoryMask,Extension->CurrentWaitRequest);
+
+ *((ULONG *)reqContext->SystemBuffer) =
+ Extension->HistoryMask;
+ Extension->HistoryMask = 0;
+ reqContext->Information = sizeof(ULONG);
+ reqContext->Status = STATUS_SUCCESS;
+
+ SerialInsertQueueDpc(Extension->CommWaitDpc);
+
+ }
+
+ return FALSE;
+}
+
+BOOLEAN
+SerialFinishOldWait(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine will check to see if the ISR still knows about
+ a wait request by checking to see if the Irpmasklocation is non-null.
+ If it is then it will zero the Irpmasklocation (which in effect
+ grabs the request away from the isr). This routine is only called
+ buy the cancel code for the wait.
+
+ NOTE: This is called by WdfInterruptSynchronize.
+
+Arguments:
+
+ Context - A pointer to the device extension
+
+Return Value:
+
+ Always FALSE.
+
+--*/
+
+{
+ PSERIAL_DEVICE_EXTENSION Extension = Context;
+
+ PREQUEST_CONTEXT reqContext = NULL;
+ PREQUEST_CONTEXT reqContextMask;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ reqContextMask = SerialGetRequestContext(Extension->CurrentMaskRequest);
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS,
+ "In SerialFinishOldWait\n");
+
+ if (Extension->IrpMaskLocation) {
+
+ reqContext = SerialGetRequestContext(Extension->CurrentWaitRequest);
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS,
+ "The isr still owns the request %p, mask "
+ "location is %p\n"
+ "------- and system buffer is %p\n",
+ Extension->CurrentWaitRequest,Extension->IrpMaskLocation,
+ reqContext->SystemBuffer);
+ //
+ // The isr still "owns" the request.
+ //
+
+ *Extension->IrpMaskLocation = 0;
+ Extension->IrpMaskLocation = NULL;
+
+ reqContext->Information = sizeof(ULONG);
+
+ //
+ // We don't decrement the reference since the completion routine
+ // will do that.
+ //
+
+ SerialInsertQueueDpc(Extension->CommWaitDpc);
+
+ }
+
+ //
+ // Don't wipe out any historical data we are still interested in.
+ //
+
+ Extension->HistoryMask &= *((ULONG *)reqContextMask->SystemBuffer);
+
+ Extension->IsrWaitMask = *((ULONG *)reqContextMask->SystemBuffer);
+ SerialDbgPrintEx( TRACE_LEVEL_INFORMATION, DBG_IOCTLS,
+ "Set mask location of %p, in request %p, with "
+ "system buffer of %p\n",
+ Extension->IrpMaskLocation, Extension->CurrentMaskRequest,
+ reqContextMask->SystemBuffer);
+ return FALSE;
+}
+
+VOID
+SerialCancelWait(
+ IN WDFREQUEST Request
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to cancel a request that is waiting on
+ a comm event.
+
+Arguments:
+
+ Device - Wdf handle for the device
+
+ Request - Pointer to the WDFREQUEST for the current request
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION Extension;
+ WDFDEVICE device = WdfIoQueueGetDevice(WdfRequestGetIoQueue(Request));
+
+ UNREFERENCED_PARAMETER(Request);
+
+ Extension = SerialGetDeviceExtension(device);
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS,
+ "Canceling wait for request %p\n",
+ Extension->CurrentWaitRequest);
+
+ SerialTryToCompleteCurrent(Extension,
+ SerialGrabWaitFromIsr,
+ STATUS_CANCELLED,
+ &Extension->CurrentWaitRequest,
+ NULL, NULL, NULL,
+ NULL, NULL, SERIAL_REF_CANCEL);
+
+}
+
+
+VOID
+SerialCompleteWait(
+ IN WDFDPC Dpc
+ )
+
+{
+
+ PSERIAL_DEVICE_EXTENSION Extension = NULL;
+
+ Extension = SerialGetDeviceExtension(WdfDpcGetParentObject(Dpc));
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS,
+ ">SerialCompleteWait(%p)\n",
+ Extension);
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS,
+ "Completing wait for request %p\n",
+ Extension->CurrentWaitRequest);
+
+ SerialTryToCompleteCurrent(Extension, NULL, STATUS_SUCCESS,
+ &Extension->CurrentWaitRequest, NULL, NULL, NULL,
+ NULL, NULL, SERIAL_REF_ISR);
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS,
+ "<SerialCompleteWait\n");
+}
+
+
diff --git a/serial/serial/wdfserial.vcxproj b/serial/serial/wdfserial.vcxproj
new file mode 100644
index 00000000..866f8317
--- /dev/null
+++ b/serial/serial/wdfserial.vcxproj
@@ -0,0 +1,389 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{B4050279-1352-4D9E-9CF6-6409E8330DF1}</ProjectGuid>
+ <RootNamespace>$(MSBuildProjectName)</RootNamespace>
+ <KMDF_VERSION_MAJOR>1</KMDF_VERSION_MAJOR>
+ <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration>
+ <Platform Condition="'$(Platform)' == ''">Win32</Platform>
+ <SampleGuid>{CCF3EBBE-8B29-4E2E-8FDF-363939305496}</SampleGuid>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>False</UseDebugLibraries>
+ <DriverTargetPlatform>Universal</DriverTargetPlatform>
+ <DriverType>KMDF</DriverType>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+ <ConfigurationType>Driver</ConfigurationType>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>True</UseDebugLibraries>
+ <DriverTargetPlatform>Universal</DriverTargetPlatform>
+ <DriverType>KMDF</DriverType>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+ <ConfigurationType>Driver</ConfigurationType>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>False</UseDebugLibraries>
+ <DriverTargetPlatform>Universal</DriverTargetPlatform>
+ <DriverType>KMDF</DriverType>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+ <ConfigurationType>Driver</ConfigurationType>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>True</UseDebugLibraries>
+ <DriverTargetPlatform>Universal</DriverTargetPlatform>
+ <DriverType>KMDF</DriverType>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+ <ConfigurationType>Driver</ConfigurationType>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <PropertyGroup>
+ <OutDir>$(IntDir)</OutDir>
+ </PropertyGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ItemGroup Label="WrappedTaskItems">
+ <ClCompile Include="error.c">
+ <WppEnabled>true</WppEnabled>
+ <WppKernelMode>true</WppKernelMode>
+ <WppTraceFunction>SerialDbgPrintEx(LEVEL,FLAGS,MSG,...)</WppTraceFunction>
+ <WppGenerateUsingTemplateFile>{km-WdfDefault.tpl}*.tmh</WppGenerateUsingTemplateFile>
+ <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreCompiledHeaderFile>precomp.h</PreCompiledHeaderFile>
+ <PreCompiledHeader>Use</PreCompiledHeader>
+ <PreCompiledHeaderOutputFile>$(IntDir)\precomp.pch</PreCompiledHeaderOutputFile>
+ </ClCompile>
+ <ClCompile Include="immediat.c">
+ <WppEnabled>true</WppEnabled>
+ <WppKernelMode>true</WppKernelMode>
+ <WppTraceFunction>SerialDbgPrintEx(LEVEL,FLAGS,MSG,...)</WppTraceFunction>
+ <WppGenerateUsingTemplateFile>{km-WdfDefault.tpl}*.tmh</WppGenerateUsingTemplateFile>
+ <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreCompiledHeaderFile>precomp.h</PreCompiledHeaderFile>
+ <PreCompiledHeader>Use</PreCompiledHeader>
+ <PreCompiledHeaderOutputFile>$(IntDir)\precomp.pch</PreCompiledHeaderOutputFile>
+ </ClCompile>
+ <ClCompile Include="initunlo.c">
+ <WppEnabled>true</WppEnabled>
+ <WppKernelMode>true</WppKernelMode>
+ <WppTraceFunction>SerialDbgPrintEx(LEVEL,FLAGS,MSG,...)</WppTraceFunction>
+ <WppGenerateUsingTemplateFile>{km-WdfDefault.tpl}*.tmh</WppGenerateUsingTemplateFile>
+ <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreCompiledHeaderFile>precomp.h</PreCompiledHeaderFile>
+ <PreCompiledHeader>Use</PreCompiledHeader>
+ <PreCompiledHeaderOutputFile>$(IntDir)\precomp.pch</PreCompiledHeaderOutputFile>
+ </ClCompile>
+ <ClCompile Include="ioctl.c">
+ <WppEnabled>true</WppEnabled>
+ <WppKernelMode>true</WppKernelMode>
+ <WppTraceFunction>SerialDbgPrintEx(LEVEL,FLAGS,MSG,...)</WppTraceFunction>
+ <WppGenerateUsingTemplateFile>{km-WdfDefault.tpl}*.tmh</WppGenerateUsingTemplateFile>
+ <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreCompiledHeaderFile>precomp.h</PreCompiledHeaderFile>
+ <PreCompiledHeader>Use</PreCompiledHeader>
+ <PreCompiledHeaderOutputFile>$(IntDir)\precomp.pch</PreCompiledHeaderOutputFile>
+ </ClCompile>
+ <ClCompile Include="isr.c">
+ <WppEnabled>true</WppEnabled>
+ <WppKernelMode>true</WppKernelMode>
+ <WppTraceFunction>SerialDbgPrintEx(LEVEL,FLAGS,MSG,...)</WppTraceFunction>
+ <WppGenerateUsingTemplateFile>{km-WdfDefault.tpl}*.tmh</WppGenerateUsingTemplateFile>
+ <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreCompiledHeaderFile>precomp.h</PreCompiledHeaderFile>
+ <PreCompiledHeader>Use</PreCompiledHeader>
+ <PreCompiledHeaderOutputFile>$(IntDir)\precomp.pch</PreCompiledHeaderOutputFile>
+ </ClCompile>
+ <ClCompile Include="modmflow.c">
+ <WppEnabled>true</WppEnabled>
+ <WppKernelMode>true</WppKernelMode>
+ <WppTraceFunction>SerialDbgPrintEx(LEVEL,FLAGS,MSG,...)</WppTraceFunction>
+ <WppGenerateUsingTemplateFile>{km-WdfDefault.tpl}*.tmh</WppGenerateUsingTemplateFile>
+ <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreCompiledHeaderFile>precomp.h</PreCompiledHeaderFile>
+ <PreCompiledHeader>Use</PreCompiledHeader>
+ <PreCompiledHeaderOutputFile>$(IntDir)\precomp.pch</PreCompiledHeaderOutputFile>
+ </ClCompile>
+ <ClCompile Include="openclos.c">
+ <WppEnabled>true</WppEnabled>
+ <WppKernelMode>true</WppKernelMode>
+ <WppTraceFunction>SerialDbgPrintEx(LEVEL,FLAGS,MSG,...)</WppTraceFunction>
+ <WppGenerateUsingTemplateFile>{km-WdfDefault.tpl}*.tmh</WppGenerateUsingTemplateFile>
+ <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreCompiledHeaderFile>precomp.h</PreCompiledHeaderFile>
+ <PreCompiledHeader>Use</PreCompiledHeader>
+ <PreCompiledHeaderOutputFile>$(IntDir)\precomp.pch</PreCompiledHeaderOutputFile>
+ </ClCompile>
+ <ClCompile Include="purge.c">
+ <WppEnabled>true</WppEnabled>
+ <WppKernelMode>true</WppKernelMode>
+ <WppTraceFunction>SerialDbgPrintEx(LEVEL,FLAGS,MSG,...)</WppTraceFunction>
+ <WppGenerateUsingTemplateFile>{km-WdfDefault.tpl}*.tmh</WppGenerateUsingTemplateFile>
+ <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreCompiledHeaderFile>precomp.h</PreCompiledHeaderFile>
+ <PreCompiledHeader>Use</PreCompiledHeader>
+ <PreCompiledHeaderOutputFile>$(IntDir)\precomp.pch</PreCompiledHeaderOutputFile>
+ </ClCompile>
+ <ClCompile Include="read.c">
+ <WppEnabled>true</WppEnabled>
+ <WppKernelMode>true</WppKernelMode>
+ <WppTraceFunction>SerialDbgPrintEx(LEVEL,FLAGS,MSG,...)</WppTraceFunction>
+ <WppGenerateUsingTemplateFile>{km-WdfDefault.tpl}*.tmh</WppGenerateUsingTemplateFile>
+ <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreCompiledHeaderFile>precomp.h</PreCompiledHeaderFile>
+ <PreCompiledHeader>Use</PreCompiledHeader>
+ <PreCompiledHeaderOutputFile>$(IntDir)\precomp.pch</PreCompiledHeaderOutputFile>
+ </ClCompile>
+ <ClCompile Include="utils.c">
+ <WppEnabled>true</WppEnabled>
+ <WppKernelMode>true</WppKernelMode>
+ <WppTraceFunction>SerialDbgPrintEx(LEVEL,FLAGS,MSG,...)</WppTraceFunction>
+ <WppGenerateUsingTemplateFile>{km-WdfDefault.tpl}*.tmh</WppGenerateUsingTemplateFile>
+ <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreCompiledHeaderFile>precomp.h</PreCompiledHeaderFile>
+ <PreCompiledHeader>Use</PreCompiledHeader>
+ <PreCompiledHeaderOutputFile>$(IntDir)\precomp.pch</PreCompiledHeaderOutputFile>
+ </ClCompile>
+ <ClCompile Include="waitmask.c">
+ <WppEnabled>true</WppEnabled>
+ <WppKernelMode>true</WppKernelMode>
+ <WppTraceFunction>SerialDbgPrintEx(LEVEL,FLAGS,MSG,...)</WppTraceFunction>
+ <WppGenerateUsingTemplateFile>{km-WdfDefault.tpl}*.tmh</WppGenerateUsingTemplateFile>
+ <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreCompiledHeaderFile>precomp.h</PreCompiledHeaderFile>
+ <PreCompiledHeader>Use</PreCompiledHeader>
+ <PreCompiledHeaderOutputFile>$(IntDir)\precomp.pch</PreCompiledHeaderOutputFile>
+ </ClCompile>
+ <ClCompile Include="write.c">
+ <WppEnabled>true</WppEnabled>
+ <WppKernelMode>true</WppKernelMode>
+ <WppTraceFunction>SerialDbgPrintEx(LEVEL,FLAGS,MSG,...)</WppTraceFunction>
+ <WppGenerateUsingTemplateFile>{km-WdfDefault.tpl}*.tmh</WppGenerateUsingTemplateFile>
+ <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreCompiledHeaderFile>precomp.h</PreCompiledHeaderFile>
+ <PreCompiledHeader>Use</PreCompiledHeader>
+ <PreCompiledHeaderOutputFile>$(IntDir)\precomp.pch</PreCompiledHeaderOutputFile>
+ </ClCompile>
+ <ClCompile Include="pnp.c">
+ <WppEnabled>true</WppEnabled>
+ <WppKernelMode>true</WppKernelMode>
+ <WppTraceFunction>SerialDbgPrintEx(LEVEL,FLAGS,MSG,...)</WppTraceFunction>
+ <WppGenerateUsingTemplateFile>{km-WdfDefault.tpl}*.tmh</WppGenerateUsingTemplateFile>
+ <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreCompiledHeaderFile>precomp.h</PreCompiledHeaderFile>
+ <PreCompiledHeader>Use</PreCompiledHeader>
+ <PreCompiledHeaderOutputFile>$(IntDir)\precomp.pch</PreCompiledHeaderOutputFile>
+ </ClCompile>
+ <ClCompile Include="registry.c">
+ <WppEnabled>true</WppEnabled>
+ <WppKernelMode>true</WppKernelMode>
+ <WppTraceFunction>SerialDbgPrintEx(LEVEL,FLAGS,MSG,...)</WppTraceFunction>
+ <WppGenerateUsingTemplateFile>{km-WdfDefault.tpl}*.tmh</WppGenerateUsingTemplateFile>
+ <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreCompiledHeaderFile>precomp.h</PreCompiledHeaderFile>
+ <PreCompiledHeader>Use</PreCompiledHeader>
+ <PreCompiledHeaderOutputFile>$(IntDir)\precomp.pch</PreCompiledHeaderOutputFile>
+ </ClCompile>
+ <ClCompile Include="power.c">
+ <WppEnabled>true</WppEnabled>
+ <WppKernelMode>true</WppKernelMode>
+ <WppTraceFunction>SerialDbgPrintEx(LEVEL,FLAGS,MSG,...)</WppTraceFunction>
+ <WppGenerateUsingTemplateFile>{km-WdfDefault.tpl}*.tmh</WppGenerateUsingTemplateFile>
+ <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreCompiledHeaderFile>precomp.h</PreCompiledHeaderFile>
+ <PreCompiledHeader>Use</PreCompiledHeader>
+ <PreCompiledHeaderOutputFile>$(IntDir)\precomp.pch</PreCompiledHeaderOutputFile>
+ </ClCompile>
+ <ClCompile Include="wmi.c">
+ <WppEnabled>true</WppEnabled>
+ <WppKernelMode>true</WppKernelMode>
+ <WppTraceFunction>SerialDbgPrintEx(LEVEL,FLAGS,MSG,...)</WppTraceFunction>
+ <WppGenerateUsingTemplateFile>{km-WdfDefault.tpl}*.tmh</WppGenerateUsingTemplateFile>
+ <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreCompiledHeaderFile>precomp.h</PreCompiledHeaderFile>
+ <PreCompiledHeader>Use</PreCompiledHeader>
+ <PreCompiledHeaderOutputFile>$(IntDir)\precomp.pch</PreCompiledHeaderOutputFile>
+ </ClCompile>
+ <ClCompile Include="log.c">
+ <WppEnabled>true</WppEnabled>
+ <WppKernelMode>true</WppKernelMode>
+ <WppTraceFunction>SerialDbgPrintEx(LEVEL,FLAGS,MSG,...)</WppTraceFunction>
+ <WppGenerateUsingTemplateFile>{km-WdfDefault.tpl}*.tmh</WppGenerateUsingTemplateFile>
+ <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreCompiledHeaderFile>precomp.h</PreCompiledHeaderFile>
+ <PreCompiledHeader>Use</PreCompiledHeader>
+ <PreCompiledHeaderOutputFile>$(IntDir)\precomp.pch</PreCompiledHeaderOutputFile>
+ </ClCompile>
+ <ClCompile Include="flush.c">
+ <WppEnabled>true</WppEnabled>
+ <WppKernelMode>true</WppKernelMode>
+ <WppTraceFunction>SerialDbgPrintEx(LEVEL,FLAGS,MSG,...)</WppTraceFunction>
+ <WppGenerateUsingTemplateFile>{km-WdfDefault.tpl}*.tmh</WppGenerateUsingTemplateFile>
+ <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreCompiledHeaderFile>precomp.h</PreCompiledHeaderFile>
+ <PreCompiledHeader>Use</PreCompiledHeader>
+ <PreCompiledHeaderOutputFile>$(IntDir)\precomp.pch</PreCompiledHeaderOutputFile>
+ </ClCompile>
+ <ClCompile Include="qsfile.c">
+ <WppEnabled>true</WppEnabled>
+ <WppKernelMode>true</WppKernelMode>
+ <WppTraceFunction>SerialDbgPrintEx(LEVEL,FLAGS,MSG,...)</WppTraceFunction>
+ <WppGenerateUsingTemplateFile>{km-WdfDefault.tpl}*.tmh</WppGenerateUsingTemplateFile>
+ <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreCompiledHeaderFile>precomp.h</PreCompiledHeaderFile>
+ <PreCompiledHeader>Use</PreCompiledHeader>
+ <PreCompiledHeaderOutputFile>$(IntDir)\precomp.pch</PreCompiledHeaderOutputFile>
+ </ClCompile>
+ <Inf Include=".\serial.inx">
+ <Architecture>$(InfArch)</Architecture>
+ <SpecifyArchitecture>true</SpecifyArchitecture>
+ <CopyOutput>.\$(IntDir)\serial.inf</CopyOutput>
+ </Inf>
+ <OtherWpp Include="serial.rc; serlog.mc">
+ <WppEnabled>true</WppEnabled>
+ <WppKernelMode>true</WppKernelMode>
+ <WppTraceFunction>SerialDbgPrintEx(LEVEL,FLAGS,MSG,...)</WppTraceFunction>
+ <WppGenerateUsingTemplateFile>{km-WdfDefault.tpl}*.tmh</WppGenerateUsingTemplateFile>
+ </OtherWpp>
+ </ItemGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <TargetName>wdfserial</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <TargetName>wdfserial</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <TargetName>wdfserial</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <TargetName>wdfserial</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Link>
+ <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\ntstrsafe.lib;$(DDK_LIB_PATH)\rtlver.lib</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Link>
+ <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\ntstrsafe.lib;$(DDK_LIB_PATH)\rtlver.lib</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Link>
+ <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\ntstrsafe.lib;$(DDK_LIB_PATH)\rtlver.lib</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Link>
+ <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\ntstrsafe.lib;$(DDK_LIB_PATH)\rtlver.lib</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);EVENT_TRACING</PreprocessorDefinitions>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ </ClCompile>
+ <Midl>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);EVENT_TRACING</PreprocessorDefinitions>
+ </Midl>
+ <ResourceCompile>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);EVENT_TRACING</PreprocessorDefinitions>
+ </ResourceCompile>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);EVENT_TRACING</PreprocessorDefinitions>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ </ClCompile>
+ <Midl>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);EVENT_TRACING</PreprocessorDefinitions>
+ </Midl>
+ <ResourceCompile>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);EVENT_TRACING</PreprocessorDefinitions>
+ </ResourceCompile>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);EVENT_TRACING</PreprocessorDefinitions>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ </ClCompile>
+ <Midl>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);EVENT_TRACING</PreprocessorDefinitions>
+ </Midl>
+ <ResourceCompile>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);EVENT_TRACING</PreprocessorDefinitions>
+ </ResourceCompile>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);EVENT_TRACING</PreprocessorDefinitions>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ </ClCompile>
+ <Midl>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);EVENT_TRACING</PreprocessorDefinitions>
+ </Midl>
+ <ResourceCompile>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);EVENT_TRACING</PreprocessorDefinitions>
+ </ResourceCompile>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="precompsrc.c">
+ <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreCompiledHeaderFile>precomp.h</PreCompiledHeaderFile>
+ <PreCompiledHeader>Create</PreCompiledHeader>
+ <PreCompiledHeaderOutputFile>$(IntDir)\precomp.pch</PreCompiledHeaderOutputFile>
+ </ClCompile>
+ <MessageCompile Include="serlog.mc" />
+ <ResourceCompile Include="serial.rc" />
+ </ItemGroup>
+ <ItemGroup>
+ <Inf Exclude="@(Inf)" Include="*.inf" />
+ <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" />
+ <FilesToPackage Include="@(Inf->'%(CopyOutput)')" Condition="'@(Inf)'!=''" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Exclude="@(None)" Include="*.txt;*.htm;*.html" />
+ <None Exclude="@(None)" Include="*.ico;*.cur;*.bmp;*.dlg;*.rct;*.gif;*.jpg;*.jpeg;*.wav;*.jpe;*.tiff;*.tif;*.png;*.rc2" />
+ <None Exclude="@(None)" Include="*.def;*.bat;*.hpj;*.asmx" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+</Project> \ No newline at end of file
diff --git a/serial/serial/wdfserial.vcxproj.Filters b/serial/serial/wdfserial.vcxproj.Filters
new file mode 100644
index 00000000..bdaa0854
--- /dev/null
+++ b/serial/serial/wdfserial.vcxproj.Filters
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions>
+ <UniqueIdentifier>{F3AB51FC-17F5-4450-9B7D-97EBDEB45FFE}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files">
+ <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
+ <UniqueIdentifier>{C94DF774-16E6-4184-8B88-1823A5717BCF}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Resource Files">
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions>
+ <UniqueIdentifier>{1B2DDEE2-3883-4D83-BF10-01F31CB52591}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Driver Files">
+ <Extensions>inf;inv;inx;mof;mc;</Extensions>
+ <UniqueIdentifier>{BAE6B924-5A31-4C39-BFA5-E5266CF030D1}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="error.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="flush.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="immediat.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="initunlo.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ioctl.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="isr.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="log.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="modmflow.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="openclos.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="pnp.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="power.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="precompsrc.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="purge.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="qsfile.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="read.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="registry.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="utils.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="waitmask.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="wmi.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="write.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <FilesToPackage Include=".\Debug\\serial.inf">
+ <Filter>Driver Files</Filter>
+ </FilesToPackage>
+ <Inf Include=".\serial.inx">
+ <Filter>Driver Files</Filter>
+ </Inf>
+ <MessageCompile Include="serlog.mc">
+ <Filter>Driver Files</Filter>
+ </MessageCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="serial.rc">
+ <Filter>Resource Files</Filter>
+ </ResourceCompile>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/serial/serial/wmi.c b/serial/serial/wmi.c
new file mode 100644
index 00000000..6d69910e
--- /dev/null
+++ b/serial/serial/wmi.c
@@ -0,0 +1,295 @@
+/*++
+
+Copyright (c) 1997 Microsoft Corporation
+
+Module Name:
+
+ wmi.c
+
+Abstract:
+
+ This module contains the code that handles the wmi IRPs for the
+ serial driver.
+
+Environment:
+
+ Kernel mode
+
+--*/
+
+#include "precomp.h"
+#include <wmistr.h>
+
+#if defined(EVENT_TRACING)
+#include "wmi.tmh"
+#endif
+
+EVT_WDF_WMI_INSTANCE_QUERY_INSTANCE EvtWmiQueryPortName;
+EVT_WDF_WMI_INSTANCE_QUERY_INSTANCE EvtWmiQueryPortCommData;
+EVT_WDF_WMI_INSTANCE_QUERY_INSTANCE EvtWmiQueryPortHWData;
+EVT_WDF_WMI_INSTANCE_QUERY_INSTANCE EvtWmiQueryPortPerfData;
+EVT_WDF_WMI_INSTANCE_QUERY_INSTANCE EvtWmiQueryPortPropData;
+
+NTSTATUS
+SerialWmiRegisterInstance(
+ WDFDEVICE Device,
+ const GUID* Guid,
+ ULONG MinInstanceBufferSize,
+ PFN_WDF_WMI_INSTANCE_QUERY_INSTANCE EvtWmiInstanceQueryInstance
+ );
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGESRP0, SerialWmiRegistration)
+#pragma alloc_text(PAGESRP0, SerialWmiRegisterInstance)
+#pragma alloc_text(PAGESRP0, EvtWmiQueryPortName)
+#pragma alloc_text(PAGESRP0, EvtWmiQueryPortCommData)
+#pragma alloc_text(PAGESRP0, EvtWmiQueryPortHWData)
+#pragma alloc_text(PAGESRP0, EvtWmiQueryPortPerfData)
+#pragma alloc_text(PAGESRP0, EvtWmiQueryPortPropData)
+#endif
+
+NTSTATUS
+SerialWmiRegisterInstance(
+ WDFDEVICE Device,
+ const GUID* Guid,
+ ULONG MinInstanceBufferSize,
+ PFN_WDF_WMI_INSTANCE_QUERY_INSTANCE EvtWmiInstanceQueryInstance
+ )
+{
+ WDF_WMI_PROVIDER_CONFIG providerConfig;
+ WDF_WMI_INSTANCE_CONFIG instanceConfig;
+
+ PAGED_CODE();
+
+ //
+ // Create and register WMI providers and instances blocks
+ //
+ WDF_WMI_PROVIDER_CONFIG_INIT(&providerConfig, Guid);
+ providerConfig.MinInstanceBufferSize = MinInstanceBufferSize;
+
+ WDF_WMI_INSTANCE_CONFIG_INIT_PROVIDER_CONFIG(&instanceConfig, &providerConfig);
+ instanceConfig.Register = TRUE;
+ instanceConfig.EvtWmiInstanceQueryInstance = EvtWmiInstanceQueryInstance;
+
+ return WdfWmiInstanceCreate(Device,
+ &instanceConfig,
+ WDF_NO_OBJECT_ATTRIBUTES,
+ WDF_NO_HANDLE);
+}
+
+NTSTATUS
+SerialWmiRegistration(
+ WDFDEVICE Device
+)
+/*++
+Routine Description
+
+ Registers with WMI as a data provider for this
+ instance of the device
+
+--*/
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PSERIAL_DEVICE_EXTENSION pDevExt;
+
+ PAGED_CODE();
+
+ pDevExt = SerialGetDeviceExtension (Device);
+
+ //
+ // Fill in wmi perf data (all zero's)
+ //
+ RtlZeroMemory(&pDevExt->WmiPerfData, sizeof(pDevExt->WmiPerfData));
+
+ status = SerialWmiRegisterInstance(Device,
+ &MSSerial_PortName_GUID,
+ 0,
+ EvtWmiQueryPortName);
+ if (!NT_SUCCESS(status)) {
+ return status;
+ }
+
+ status = SerialWmiRegisterInstance(Device,
+ &MSSerial_CommInfo_GUID,
+ sizeof(SERIAL_WMI_COMM_DATA),
+ EvtWmiQueryPortCommData);
+ if (!NT_SUCCESS(status)) {
+ return status;
+ }
+
+ status = SerialWmiRegisterInstance(Device,
+ &MSSerial_HardwareConfiguration_GUID,
+ sizeof(SERIAL_WMI_HW_DATA),
+ EvtWmiQueryPortHWData);
+ if (!NT_SUCCESS(status)) {
+ return status;
+ }
+
+ status = SerialWmiRegisterInstance(Device,
+ &MSSerial_PerformanceInformation_GUID,
+ sizeof(SERIAL_WMI_PERF_DATA),
+ EvtWmiQueryPortPerfData);
+ if (!NT_SUCCESS(status)) {
+ return status;
+ }
+
+ status = SerialWmiRegisterInstance(Device,
+ &MSSerial_CommProperties_GUID,
+ sizeof(SERIAL_COMMPROP) + sizeof(ULONG),
+ EvtWmiQueryPortPropData);
+
+ if (!NT_SUCCESS(status)) {
+ return status;
+ }
+
+ return status;
+}
+
+//
+// WMI Call back functions
+//
+
+NTSTATUS
+EvtWmiQueryPortName(
+ IN WDFWMIINSTANCE WmiInstance,
+ IN ULONG OutBufferSize,
+ IN PVOID OutBuffer,
+ OUT PULONG BufferUsed
+ )
+{
+ WDFDEVICE device;
+ WCHAR pRegName[SYMBOLIC_NAME_LENGTH];
+ UNICODE_STRING string;
+ USHORT nameSize = sizeof(pRegName);
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ device = WdfWmiInstanceGetDevice(WmiInstance);
+
+ status = SerialReadSymName(device, pRegName, &nameSize);
+ if (!NT_SUCCESS(status)) {
+ return status;
+ }
+
+ RtlInitUnicodeString(&string, pRegName);
+
+ return WDF_WMI_BUFFER_APPEND_STRING(OutBuffer,
+ OutBufferSize,
+ &string,
+ BufferUsed);
+}
+
+NTSTATUS
+EvtWmiQueryPortCommData(
+ IN WDFWMIINSTANCE WmiInstance,
+ IN ULONG OutBufferSize,
+ IN PVOID OutBuffer,
+ OUT PULONG BufferUsed
+ )
+{
+ PSERIAL_DEVICE_EXTENSION pDevExt;
+
+ UNREFERENCED_PARAMETER(OutBufferSize);
+
+ PAGED_CODE();
+
+ pDevExt = SerialGetDeviceExtension (WdfWmiInstanceGetDevice(WmiInstance));
+
+ *BufferUsed = sizeof(SERIAL_WMI_COMM_DATA);
+
+ if (OutBufferSize < *BufferUsed) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ *(PSERIAL_WMI_COMM_DATA)OutBuffer = pDevExt->WmiCommData;
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+EvtWmiQueryPortHWData(
+ IN WDFWMIINSTANCE WmiInstance,
+ IN ULONG OutBufferSize,
+ IN PVOID OutBuffer,
+ OUT PULONG BufferUsed
+ )
+{
+ PSERIAL_DEVICE_EXTENSION pDevExt;
+
+ UNREFERENCED_PARAMETER(OutBufferSize);
+
+ PAGED_CODE();
+
+ pDevExt = SerialGetDeviceExtension (WdfWmiInstanceGetDevice(WmiInstance));
+
+ *BufferUsed = sizeof(SERIAL_WMI_HW_DATA);
+
+ if (OutBufferSize < *BufferUsed) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ *(PSERIAL_WMI_HW_DATA)OutBuffer = pDevExt->WmiHwData;
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+EvtWmiQueryPortPerfData(
+ IN WDFWMIINSTANCE WmiInstance,
+ IN ULONG OutBufferSize,
+ IN PVOID OutBuffer,
+ OUT PULONG BufferUsed
+ )
+{
+ PSERIAL_DEVICE_EXTENSION pDevExt;
+
+ UNREFERENCED_PARAMETER(OutBufferSize);
+
+ PAGED_CODE();
+
+ pDevExt = SerialGetDeviceExtension (WdfWmiInstanceGetDevice(WmiInstance));
+
+ *BufferUsed = sizeof(SERIAL_WMI_PERF_DATA);
+
+ if (OutBufferSize < *BufferUsed) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ *(PSERIAL_WMI_PERF_DATA)OutBuffer = pDevExt->WmiPerfData;
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+EvtWmiQueryPortPropData(
+ IN WDFWMIINSTANCE WmiInstance,
+ IN ULONG OutBufferSize,
+ IN PVOID OutBuffer,
+ OUT PULONG BufferUsed
+ )
+{
+ PSERIAL_DEVICE_EXTENSION pDevExt;
+
+ UNREFERENCED_PARAMETER(OutBufferSize);
+
+ PAGED_CODE();
+
+ pDevExt = SerialGetDeviceExtension (WdfWmiInstanceGetDevice(WmiInstance));
+
+ *BufferUsed = sizeof(SERIAL_COMMPROP) + sizeof(ULONG);
+
+ if (OutBufferSize < *BufferUsed) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ SerialGetProperties(
+ pDevExt,
+ (PSERIAL_COMMPROP)OutBuffer
+ );
+
+ *((PULONG)(((PSERIAL_COMMPROP)OutBuffer)->ProvChar)) = 0;
+
+ return STATUS_SUCCESS;
+}
+
diff --git a/serial/serial/write.c b/serial/serial/write.c
new file mode 100644
index 00000000..ae7eb69e
--- /dev/null
+++ b/serial/serial/write.c
@@ -0,0 +1,1195 @@
+/*++
+
+Copyright (c) Microsoft Corporation
+
+Module Name:
+
+ write.c
+
+Abstract:
+
+ This module contains the code that is very specific to write
+ operations in the serial driver
+
+Environment:
+
+ Kernel mode
+
+--*/
+
+#include "precomp.h"
+
+#if defined(EVENT_TRACING)
+#include "write.tmh"
+#endif
+
+EVT_WDF_REQUEST_CANCEL SerialCancelCurrentWrite;
+EVT_WDF_REQUEST_CANCEL SerialCancelCurrentXoff;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialGiveWriteToIsr;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialGiveXoffToIsr;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialGrabWriteFromIsr;
+EVT_WDF_INTERRUPT_SYNCHRONIZE SerialGrabXoffFromIsr;
+
+
+VOID
+SerialEvtIoWrite(
+ IN WDFQUEUE Queue,
+ IN WDFREQUEST Request,
+ IN size_t Length
+ )
+
+/*++
+
+Routine Description:
+
+ This is the dispatch routine for write. It validates the parameters
+ for the write request and if all is ok then it places the request
+ on the work queue.
+
+Arguments:
+
+ Queue - Handle to the framework queue object that is associated
+ with the I/O request.
+ Request - Pointer to the WDFREQUEST for the current request
+
+ Length - Length of the IO operation
+ The default property of the queue is to not dispatch
+ zero lenght read & write requests to the driver and
+ complete is with status success. So we will never get
+ a zero length request.
+
+Return Value:
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION extension;
+ NTSTATUS status;
+ WDFDEVICE hDevice;
+ WDF_REQUEST_PARAMETERS params;
+ PREQUEST_CONTEXT reqContext;
+ size_t bufLen;
+
+ hDevice = WdfIoQueueGetDevice(Queue);
+ extension = SerialGetDeviceExtension(hDevice);
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_WRITE,
+ ">SerialEvtIoWrite(%p, 0x%I64x)\n", Request, Length);
+
+ if (SerialCompleteIfError(extension, Request) != STATUS_SUCCESS) {
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_WRITE, "<SerialEvtIoWrite (2) %d\n", STATUS_CANCELLED);
+ return;
+
+ }
+
+
+ WDF_REQUEST_PARAMETERS_INIT(&params);
+
+ WdfRequestGetParameters(
+ Request,
+ &params
+ );
+
+ //
+ // Initialize the scratch area of the request.
+ //
+ reqContext = SerialGetRequestContext(Request);
+ reqContext->MajorFunction = params.Type;
+ reqContext->Length = (ULONG) Length;
+
+ status = WdfRequestRetrieveInputBuffer (Request, Length, &reqContext->SystemBuffer, &bufLen);
+
+ if (!NT_SUCCESS (status)) {
+
+ SerialCompleteRequest(Request , status, 0);
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_WRITE, "<SerialEvtIoWrite (4) %X\n", status);
+ return;
+ }
+
+ SerialStartOrQueue(extension, Request, extension->WriteQueue,
+ &extension->CurrentWriteRequest,
+ SerialStartWrite);
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_WRITE, "<SerialEvtIoWrite (5) %X\n", status);
+
+ return ;
+
+}
+
+VOID
+SerialStartWrite(
+ IN PSERIAL_DEVICE_EXTENSION Extension
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to start off any write. It initializes
+ the Iostatus fields of the request. It will set up any timers
+ that are used to control the write.
+
+Arguments:
+
+ Extension - Points to the serial device extension
+
+Return Value:
+
+--*/
+
+{
+
+ LARGE_INTEGER TotalTime;
+ BOOLEAN UseATimer;
+ SERIAL_TIMEOUTS Timeouts;
+ PREQUEST_CONTEXT reqContext;
+ PREQUEST_CONTEXT reqContextXoff;
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_WRITE,
+ ">SerialStartWrite(%p)\n", Extension);
+
+ TotalTime.QuadPart = 0;
+
+ do {
+
+ reqContext = SerialGetRequestContext(Extension->CurrentWriteRequest);
+
+ //
+ // If there is an xoff counter then complete it.
+ //
+
+ //
+ // We see if there is a actually an Xoff counter request.
+ //
+ // If there is, we put the write request back on the head
+ // of the write list. We then complete the xoff counter.
+ // The xoff counter completing code will actually make the
+ // xoff counter back into the current write request, and
+ // in the course of completing the xoff (which is now
+ // the current write) we will restart this request.
+ //
+
+ if (Extension->CurrentXoffRequest) {
+
+ reqContextXoff =
+ SerialGetRequestContext(Extension->CurrentXoffRequest);
+
+ if (SERIAL_REFERENCE_COUNT(reqContextXoff)) {
+
+ //
+ // The reference count is non-zero. This implies that
+ // the xoff request has not made it through the completion
+ // path yet. We will increment the reference count
+ // and attempt to complete it ourseleves.
+ //
+
+ SERIAL_SET_REFERENCE(
+ reqContextXoff,
+ SERIAL_REF_XOFF_REF
+ );
+
+ reqContextXoff->Information = 0;
+
+ //
+ // The following call will actually release the
+ // cancel spin lock.
+ //
+
+ SerialTryToCompleteCurrent(
+ Extension,
+ SerialGrabXoffFromIsr,
+ STATUS_SERIAL_MORE_WRITES,
+ &Extension->CurrentXoffRequest,
+ NULL,
+ NULL,
+ Extension->XoffCountTimer,
+ NULL,
+ NULL,
+ SERIAL_REF_XOFF_REF
+ );
+
+ } else {
+
+ //
+ // The request is well on its way to being finished.
+ // We can let the regular completion code do the
+ // work. Just release the spin lock.
+ //
+
+ }
+
+ }
+
+ UseATimer = FALSE;
+
+ //
+ // Calculate the timeout value needed for the
+ // request. Note that the values stored in the
+ // timeout record are in milliseconds. Note that
+ // if the timeout values are zero then we won't start
+ // the timer.
+ //
+
+ Timeouts = Extension->Timeouts;
+
+ if (Timeouts.WriteTotalTimeoutConstant ||
+ Timeouts.WriteTotalTimeoutMultiplier) {
+
+ UseATimer = TRUE;
+
+ //
+ // We have some timer values to calculate.
+ //
+ // Take care, we might have an xoff counter masquerading
+ // as a write.
+ //
+
+ TotalTime.QuadPart =
+ ((LONGLONG)((UInt32x32To64(
+ (reqContext->MajorFunction == IRP_MJ_WRITE)?
+ (reqContext->Length) : (1),
+ Timeouts.WriteTotalTimeoutMultiplier
+ )
+ + Timeouts.WriteTotalTimeoutConstant)))
+ * -10000;
+
+ }
+
+ //
+ // The request may be going to the isr shortly. Now
+ // is a good time to initialize its reference counts.
+ //
+
+ SERIAL_INIT_REFERENCE(reqContext);
+
+ //
+ // We give the request to to the isr to write out.
+ // We set a cancel routine that knows how to
+ // grab the current write away from the isr.
+ //
+ SerialSetCancelRoutine(Extension->CurrentWriteRequest,
+ SerialCancelCurrentWrite);
+
+ if (UseATimer) {
+ BOOLEAN result;
+
+ result = SerialSetTimer(
+ Extension->WriteRequestTotalTimer,
+ TotalTime
+ );
+ if(result == FALSE) {
+ //
+ // This timer now has a reference to the request.
+ //
+
+ SERIAL_SET_REFERENCE( reqContext, SERIAL_REF_TOTAL_TIMER );
+ }
+ }
+
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SerialGiveWriteToIsr,
+ Extension
+ );
+
+ } WHILE (FALSE);
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_WRITE, "<SerialStartWrite \n");
+
+ return;
+}
+
+
+VOID
+SerialGetNextWrite(
+ IN WDFREQUEST *CurrentOpRequest,
+ IN WDFQUEUE QueueToProcess,
+ IN WDFREQUEST *NewRequest,
+ IN BOOLEAN CompleteCurrent,
+ PSERIAL_DEVICE_EXTENSION Extension
+ )
+
+/*++
+
+Routine Description:
+
+ This routine completes the old write as well as getting
+ a pointer to the next write.
+
+ The reason that we have have pointers to the current write
+ queue as well as the current write request is so that this
+ routine may be used in the common completion code for
+ read and write.
+
+Arguments:
+
+ CurrentOpRequest - Pointer to the pointer that points to the
+ current write request.
+
+ QueueToProcess - Pointer to the write queue.
+
+ NewRequest - A pointer to a pointer to the request that will be the
+ current request. Note that this could end up pointing
+ to a null pointer. This does NOT necessaryly mean
+ that there is no current write. What could occur
+ is that while the cancel lock is held the write
+ queue ended up being empty, but as soon as we release
+ the cancel spin lock a new request came in from
+ SerialStartWrite.
+
+ CompleteCurrent - Flag indicates whether the CurrentOpRequest should
+ be completed.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PREQUEST_CONTEXT reqContext;
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_WRITE, ">SerialGetNextWrite\n");
+
+
+ do {
+
+ reqContext = SerialGetRequestContext(*CurrentOpRequest);
+
+ //
+ // We could be completing a flush.
+ //
+
+ if (reqContext->MajorFunction == IRP_MJ_WRITE) {
+
+ ASSERT(Extension->TotalCharsQueued >= reqContext->Length);
+
+ Extension->TotalCharsQueued -= reqContext->Length;
+
+ } else if (reqContext->MajorFunction == IRP_MJ_DEVICE_CONTROL) {
+
+ WDFREQUEST request = *CurrentOpRequest;
+ PSERIAL_XOFF_COUNTER Xc;
+
+ Xc = reqContext->SystemBuffer;
+
+ //
+ // We should never have a xoff counter when we
+ // get to this point.
+ //
+
+ ASSERT(!Extension->CurrentXoffRequest);
+
+ //
+ // This could only be a xoff counter masquerading as
+ // a write request.
+ //
+
+ Extension->TotalCharsQueued--;
+
+ //
+ // Check to see of the xoff request has been set with success.
+ // This means that the write completed normally. If that
+ // is the case, and it hasn't been set to cancel in the
+ // meanwhile, then go on and make it the CurrentXoffRequest.
+ //
+
+ if (reqContext->Status != STATUS_SUCCESS || reqContext->Cancelled) {
+
+ // TODO: I see Xoff request getting abandoned due to loss of
+ // Total timer - SERIAL_REF_TOTAL_TIMER
+ //
+ // Oh well, we can just finish it off.
+ //
+ NOTHING;
+
+ } else {
+
+ SerialSetCancelRoutine(request, SerialCancelCurrentXoff);
+
+ //
+ // We don't want to complete the current request now. This
+ // will now get completed by the Xoff counter code.
+ //
+
+ CompleteCurrent = FALSE;
+
+ //
+ // Give the counter to the isr.
+ //
+
+ Extension->CurrentXoffRequest = request;
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SerialGiveXoffToIsr,
+ Extension
+ );
+
+ //
+ // Start the timer for the counter and increment
+ // the reference count since the timer has a
+ // reference to the request.
+ //
+
+ if (Xc->Timeout) {
+
+ LARGE_INTEGER delta;
+ BOOLEAN result;
+
+ delta.QuadPart = -((LONGLONG)UInt32x32To64(
+ 1000,
+ Xc->Timeout
+ ));
+
+ result = SerialSetTimer(
+ Extension->XoffCountTimer,
+ delta
+
+ );
+ if(result == FALSE) {
+ SERIAL_SET_REFERENCE(
+ reqContext,
+ SERIAL_REF_TOTAL_TIMER
+ );
+ }
+ }
+
+ }
+
+
+ }
+
+ //
+ // Note that the following call will (probably) also cause
+ // the current request to be completed.
+ //
+
+ SerialGetNextRequest(
+ CurrentOpRequest,
+ QueueToProcess,
+ NewRequest,
+ CompleteCurrent,
+ Extension
+ );
+
+ if (!*NewRequest) {
+
+
+ WdfInterruptSynchronize(
+ Extension->WdfInterrupt,
+ SerialProcessEmptyTransmit,
+ Extension
+ );
+
+ break;
+
+ } else if (SerialGetRequestContext(*NewRequest)->MajorFunction
+ == IRP_MJ_FLUSH_BUFFERS) {
+
+ //
+ // If we encounter a flush request we just want to get
+ // the next request and complete the flush.
+ //
+ // Note that if NewRequest is non-null then it is also
+ // equal to CurrentWriteRequest.
+ //
+
+
+ ASSERT((*NewRequest) == (*CurrentOpRequest));
+ SerialGetRequestContext(*NewRequest)->Status = STATUS_SUCCESS;
+
+ } else {
+
+ break;
+
+ }
+
+ } WHILE (TRUE);
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_WRITE, "<SerialGetNextWrite\n");
+
+}
+
+
+VOID
+SerialCompleteWrite(
+ IN WDFDPC Dpc
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is merely used to complete any write. It
+ assumes that the status and the information fields of
+ the request are already correctly filled in.
+
+Arguments:
+
+ Dpc - Not Used.
+
+ DeferredContext - Really points to the device extension.
+
+ SystemContext1 - Not Used.
+
+ SystemContext2 - Not Used.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION Extension = NULL;
+
+ Extension = SerialGetDeviceExtension(WdfDpcGetParentObject(Dpc));
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_WRITE, ">SerialCompleteWrite(%p)\n",
+ Extension);
+
+
+ SerialTryToCompleteCurrent(Extension, NULL, STATUS_SUCCESS,
+ &Extension->CurrentWriteRequest,
+ Extension->WriteQueue, NULL,
+ Extension->WriteRequestTotalTimer,
+ SerialStartWrite, SerialGetNextWrite,
+ SERIAL_REF_ISR);
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_WRITE, "<SerialCompleteWrite\n");
+
+}
+
+
+BOOLEAN
+SerialProcessEmptyTransmit(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to determine if conditions are appropriate
+ to satisfy a wait for transmit empty event, and if so to complete
+ the request that is waiting for that event. It also call the code
+ that checks to see if we should lower the RTS line if we are
+ doing transmit toggling.
+
+ NOTE: This routine is called by WdfInterruptSynchronize.
+
+ NOTE: This routine assumes that it is called with the cancel
+ spinlock held.
+
+Arguments:
+
+ Context - Really a pointer to the device extension.
+
+Return Value:
+
+ This routine always returns FALSE.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION Extension = Context;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ if (Extension->IsrWaitMask && (Extension->IsrWaitMask & SERIAL_EV_TXEMPTY) &&
+ Extension->EmptiedTransmit && (!Extension->TransmitImmediate) &&
+ (!Extension->CurrentWriteRequest) && IsQueueEmpty(Extension->WriteQueue)) {
+
+ Extension->HistoryMask |= SERIAL_EV_TXEMPTY;
+ if (Extension->IrpMaskLocation) {
+
+ *Extension->IrpMaskLocation = Extension->HistoryMask;
+ Extension->IrpMaskLocation = NULL;
+ Extension->HistoryMask = 0;
+
+ SerialGetRequestContext(Extension->CurrentWaitRequest)->Information = sizeof(ULONG);
+ SerialInsertQueueDpc(
+ Extension->CommWaitDpc
+ );
+
+ }
+
+ Extension->CountOfTryingToLowerRTS++;
+ SerialPerhapsLowerRTS(Extension->WdfInterrupt, Extension);
+
+ }
+
+ return FALSE;
+
+}
+
+
+BOOLEAN
+SerialGiveWriteToIsr(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ Try to start off the write by slipping it in behind
+ a transmit immediate char, or if that isn't available
+ and the transmit holding register is empty, "tickle"
+ the UART into interrupting with a transmit buffer
+ empty.
+
+ NOTE: This routine is called by WdfInterruptSynchronize.
+
+ NOTE: This routine assumes that it is called with the
+ cancel spin lock held.
+
+Arguments:
+
+ Context - Really a pointer to the device extension.
+
+Return Value:
+
+ This routine always returns FALSE.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION Extension = Context;
+
+ //
+ // The current stack location. This contains all of the
+ // information we need to process this particular request.
+ //
+ PREQUEST_CONTEXT reqContext;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ reqContext = SerialGetRequestContext(Extension->CurrentWriteRequest);
+
+ //
+ // We might have a xoff counter request masquerading as a
+ // write. The length of these requests will always be one
+ // and we can get a pointer to the actual character from
+ // the data supplied by the user.
+ //
+
+ if (reqContext->MajorFunction == IRP_MJ_WRITE) {
+
+ Extension->WriteLength = reqContext->Length;
+ Extension->WriteCurrentChar = reqContext->SystemBuffer;
+
+ } else {
+
+ Extension->WriteLength = 1;
+ Extension->WriteCurrentChar =
+ ((PUCHAR)reqContext->SystemBuffer) +
+ FIELD_OFFSET(
+ SERIAL_XOFF_COUNTER,
+ XoffChar
+ );
+
+ }
+
+ //
+ // The isr now has a reference to the request.
+ //
+
+ SERIAL_SET_REFERENCE(
+ reqContext,
+ SERIAL_REF_ISR
+ );
+
+ //
+ // Check first to see if an immediate char is transmitting.
+ // If it is then we'll just slip in behind it when its
+ // done.
+ //
+
+ if (!Extension->TransmitImmediate) {
+
+ //
+ // If there is no immediate char transmitting then we
+ // will "re-enable" the transmit holding register empty
+ // interrupt. The 8250 family of devices will always
+ // signal a transmit holding register empty interrupt
+ // *ANY* time this bit is set to one. By doing things
+ // this way we can simply use the normal interrupt code
+ // to start off this write.
+ //
+ // We've been keeping track of whether the transmit holding
+ // register is empty so it we only need to do this
+ // if the register is empty.
+ //
+
+ if (Extension->HoldingEmpty) {
+
+ DISABLE_ALL_INTERRUPTS(Extension, Extension->Controller);
+ ENABLE_ALL_INTERRUPTS(Extension, Extension->Controller);
+
+ }
+
+ }
+
+ //
+ // The rts line may already be up from previous writes,
+ // however, it won't take much additional time to turn
+ // on the RTS line if we are doing transmit toggling.
+ //
+
+ if ((Extension->HandFlow.FlowReplace & SERIAL_RTS_MASK) ==
+ SERIAL_TRANSMIT_TOGGLE) {
+
+ SerialSetRTS(Extension->WdfInterrupt, Extension);
+
+ }
+
+ return FALSE;
+
+}
+
+
+VOID
+SerialCancelCurrentWrite(
+ IN WDFREQUEST Request
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to cancel the current write.
+
+Arguments:
+
+ Device - Wdf handle for the device
+
+ Request - Pointer to the WDFREQUEST to be canceled.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION Extension;
+ WDFDEVICE device = WdfIoQueueGetDevice(WdfRequestGetIoQueue(Request));
+
+ UNREFERENCED_PARAMETER(Request);
+
+ Extension = SerialGetDeviceExtension(device);
+
+ SerialTryToCompleteCurrent(
+ Extension,
+ SerialGrabWriteFromIsr,
+ STATUS_CANCELLED,
+ &Extension->CurrentWriteRequest,
+ Extension->WriteQueue,
+ NULL,
+ Extension->WriteRequestTotalTimer,
+ SerialStartWrite,
+ SerialGetNextWrite,
+ SERIAL_REF_CANCEL
+ );
+
+}
+
+
+VOID
+SerialWriteTimeout(
+ IN WDFTIMER Timer
+ )
+
+/*++
+
+Routine Description:
+
+ This routine will try to timeout the current write.
+
+Arguments:
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION Extension = NULL;
+
+ Extension = SerialGetDeviceExtension(WdfTimerGetParentObject(Timer));
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_WRITE, ">SerialWriteTimeout(%p)\n",
+ Extension);
+
+ SerialTryToCompleteCurrent(Extension, SerialGrabWriteFromIsr,
+ STATUS_TIMEOUT, &Extension->CurrentWriteRequest,
+ Extension->WriteQueue, NULL,
+ Extension->WriteRequestTotalTimer,
+ SerialStartWrite, SerialGetNextWrite,
+ SERIAL_REF_TOTAL_TIMER);
+
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_WRITE, "<SerialWriteTimeout\n");
+}
+
+
+BOOLEAN
+SerialGrabWriteFromIsr(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+
+ This routine is used to grab the current request, which could be timing
+ out or canceling, from the ISR
+
+ NOTE: This routine is being called from WdfInterruptSynchronize.
+
+ NOTE: This routine assumes that the cancel spin lock is held
+ when this routine is called.
+
+Arguments:
+
+ Context - Really a pointer to the device extension.
+
+Return Value:
+
+ Always false.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION Extension = Context;
+
+ PREQUEST_CONTEXT reqContext;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ reqContext = SerialGetRequestContext(Extension->CurrentWriteRequest);
+
+ //
+ // Check if the write length is non-zero. If it is non-zero
+ // then the ISR still owns the request. We calculate the the number
+ // of characters written and update the information field of the
+ // request with the characters written. We then clear the write length
+ // the isr sees.
+ //
+
+ if (Extension->WriteLength) {
+
+ //
+ // We could have an xoff counter masquerading as a
+ // write request. If so, don't update the write length.
+ //
+
+ if (reqContext->MajorFunction == IRP_MJ_WRITE) {
+
+ reqContext->Information = reqContext->Length -Extension->WriteLength;
+
+ } else {
+
+ reqContext->Information = 0;
+
+ }
+
+ //
+ // Since the isr no longer references this request, we can
+ // decrement it's reference count.
+ //
+
+ SERIAL_CLEAR_REFERENCE(
+ reqContext,
+ SERIAL_REF_ISR
+ );
+
+ Extension->WriteLength = 0;
+
+ }
+
+ return FALSE;
+
+}
+
+
+BOOLEAN
+SerialGrabXoffFromIsr(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to grab an xoff counter request from the
+ isr when it is no longer masquerading as a write request. This
+ routine is called by the cancel and timeout code for the
+ xoff counter ioctl.
+
+
+ NOTE: This routine is being called from WdfInterruptSynchronize.
+
+ NOTE: This routine assumes that the cancel spin lock is held
+ when this routine is called.
+
+Arguments:
+
+ Context - Really a pointer to the device extension.
+
+Return Value:
+
+ Always false.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION Extension = Context;
+
+ PREQUEST_CONTEXT reqContext;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ reqContext = SerialGetRequestContext(Extension->CurrentXoffRequest);
+
+ if (Extension->CountSinceXoff) {
+
+ //
+ // This is only non-zero when there actually is a Xoff ioctl
+ // counting down.
+ //
+
+ Extension->CountSinceXoff = 0;
+
+ //
+ // We decrement the count since the isr no longer owns
+ // the request.
+ //
+
+ SERIAL_CLEAR_REFERENCE(
+ reqContext,
+ SERIAL_REF_ISR
+ );
+
+ }
+
+ return FALSE;
+
+}
+
+
+VOID
+SerialCompleteXoff(
+ IN WDFDPC Dpc
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is merely used to truely complete an xoff counter request. It
+ assumes that the status and the information fields of the request are
+ already correctly filled in.
+
+Arguments:
+
+ Dpc - Not Used.
+
+ DeferredContext - Really points to the device extension.
+
+ SystemContext1 - Not Used.
+
+ SystemContext2 - Not Used.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION Extension = NULL;
+
+ Extension = SerialGetDeviceExtension(WdfDpcGetParentObject(Dpc));
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_WRITE, ">SerialCompleteXoff(%p)\n",
+ Extension);
+
+
+ SerialTryToCompleteCurrent(Extension, NULL, STATUS_SUCCESS,
+ &Extension->CurrentXoffRequest, NULL, NULL,
+ Extension->XoffCountTimer, NULL, NULL,
+ SERIAL_REF_ISR);
+
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_WRITE, "<SerialCompleteXoff\n");
+
+}
+
+
+VOID
+SerialTimeoutXoff(
+ IN WDFTIMER Timer
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is merely used to truely complete an xoff counter request,
+ if its timer has run out.
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION Extension = NULL;
+
+ Extension = SerialGetDeviceExtension(WdfTimerGetParentObject(Timer));
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_WRITE, ">SerialTimeoutXoff(%p)\n", Extension);
+
+ SerialTryToCompleteCurrent(Extension, SerialGrabXoffFromIsr,
+ STATUS_SERIAL_COUNTER_TIMEOUT,
+ &Extension->CurrentXoffRequest, NULL, NULL, NULL,
+ NULL, NULL, SERIAL_REF_TOTAL_TIMER);
+
+ SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_WRITE, "<SerialTimeoutXoff\n");
+}
+
+
+VOID
+SerialCancelCurrentXoff(
+ IN WDFREQUEST Request
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to cancel the current write.
+
+Arguments:
+
+ Device - Wdf handle for the device
+
+ Request - Pointer to the WDFREQUEST to be canceled.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION Extension;
+ WDFDEVICE device = WdfIoQueueGetDevice(WdfRequestGetIoQueue(Request));
+
+ UNREFERENCED_PARAMETER(Request);
+
+ Extension = SerialGetDeviceExtension(device);
+
+ SerialTryToCompleteCurrent(
+ Extension,
+ SerialGrabXoffFromIsr,
+ STATUS_CANCELLED,
+ &Extension->CurrentXoffRequest,
+ NULL,
+ NULL,
+ Extension->XoffCountTimer,
+ NULL,
+ NULL,
+ SERIAL_REF_CANCEL
+ );
+
+}
+
+
+BOOLEAN
+SerialGiveXoffToIsr(
+ IN WDFINTERRUPT Interrupt,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+
+ This routine starts off the xoff counter. It merely
+ has to set the xoff count and increment the reference
+ count to denote that the isr has a reference to the request.
+
+ NOTE: This routine is called by WdfInterruptSynchronize.
+
+ NOTE: This routine assumes that it is called with the
+ cancel spin lock held.
+
+Arguments:
+
+ Context - Really a pointer to the device extension.
+
+Return Value:
+
+ This routine always returns FALSE.
+
+--*/
+
+{
+
+ PSERIAL_DEVICE_EXTENSION Extension = Context;
+ PREQUEST_CONTEXT reqContext;
+ PSERIAL_XOFF_COUNTER Xc = NULL;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ reqContext = SerialGetRequestContext(Extension->CurrentXoffRequest);
+ Xc = reqContext->SystemBuffer;
+
+ //
+ // The current stack location. This contains all of the
+ // information we need to process this particular request.
+ //
+
+ ASSERT(Extension->CurrentXoffRequest);
+ Extension->CountSinceXoff = Xc->Counter;
+
+ //
+ // The isr now has a reference to the request.
+ //
+
+ SERIAL_SET_REFERENCE(
+ reqContext,
+ SERIAL_REF_ISR
+ );
+
+ return FALSE;
+
+}
+
+