summaryrefslogtreecommitdiff
path: root/serial/VirtualSerial
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/VirtualSerial
parentef1905bf1e8825bb31120dfb27e0daf3154d859a (diff)
Initial publish
Diffstat (limited to 'serial/VirtualSerial')
-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
25 files changed, 5153 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)