diff options
| author | Dave Wilson <[email protected]> | 2015-03-17 19:50:07 -0700 |
|---|---|---|
| committer | Dave Wilson <[email protected]> | 2015-03-17 19:50:07 -0700 |
| commit | 97cf5197cf5b882b2c689d8dc2b555f2edf8f418 (patch) | |
| tree | 46f3701832d70b420eb0fc0eb93261f9da45db3f /serial/VirtualSerial | |
| parent | ef1905bf1e8825bb31120dfb27e0daf3154d859a (diff) | |
Initial publish
Diffstat (limited to 'serial/VirtualSerial')
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(¤tCharacter, 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) |
