diff --git a/.gitignore b/.gitignore index 42c50dc..02c57f9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. - +.DS_Store # User-specific files *.suo *.user @@ -259,4 +259,4 @@ paket-files/ # Python Tools for Visual Studio (PTVS) __pycache__/ -*.pyc \ No newline at end of file +*.pyc diff --git a/SharpCAT.sln b/SharpCAT.sln index 3887101..8b836a1 100644 --- a/SharpCAT.sln +++ b/SharpCAT.sln @@ -1,14 +1,10 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.28307.421 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31112.23 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SharpCATLib", "SharpCAT\SharpCATLib.csproj", "{DAD3E7BE-905A-4768-A695-0BCF96171E35}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpCATConsole", "SharpCATConsole\SharpCATConsole.csproj", "{C0C9250E-EFCE-4F8F-8249-345647440EB9}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpCATService", "SharpCATService\SharpCATService.csproj", "{8075C09F-03EE-40B2-A7DF-22F39AFCD40F}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -19,14 +15,6 @@ Global {DAD3E7BE-905A-4768-A695-0BCF96171E35}.Debug|Any CPU.Build.0 = Debug|Any CPU {DAD3E7BE-905A-4768-A695-0BCF96171E35}.Release|Any CPU.ActiveCfg = Release|Any CPU {DAD3E7BE-905A-4768-A695-0BCF96171E35}.Release|Any CPU.Build.0 = Release|Any CPU - {C0C9250E-EFCE-4F8F-8249-345647440EB9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C0C9250E-EFCE-4F8F-8249-345647440EB9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C0C9250E-EFCE-4F8F-8249-345647440EB9}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C0C9250E-EFCE-4F8F-8249-345647440EB9}.Release|Any CPU.Build.0 = Release|Any CPU - {8075C09F-03EE-40B2-A7DF-22F39AFCD40F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8075C09F-03EE-40B2-A7DF-22F39AFCD40F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8075C09F-03EE-40B2-A7DF-22F39AFCD40F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8075C09F-03EE-40B2-A7DF-22F39AFCD40F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/SharpCAT/Models/CATRadio.cs b/SharpCAT/Models/CATRadio.cs index b347cae..b6c06a0 100644 --- a/SharpCAT/Models/CATRadio.cs +++ b/SharpCAT/Models/CATRadio.cs @@ -1,7 +1,7 @@ namespace SharpCATLib.Models { //Base Radio Model - public partial class CATRadio + public partial class CATRadio : IRadio { private string RadioMfg { get; } private string RadioModel { get; } diff --git a/SharpCAT/Models/CIVRadio.cs b/SharpCAT/Models/CIVRadio.cs index c0252ef..e1f359c 100644 --- a/SharpCAT/Models/CIVRadio.cs +++ b/SharpCAT/Models/CIVRadio.cs @@ -1,6 +1,6 @@ namespace SharpCATLib.Models { - internal class CIVRadio + internal class CIVRadio : IRadio { } } \ No newline at end of file diff --git a/SharpCAT/Models/IRadio.cs b/SharpCAT/Models/IRadio.cs index 0735bf0..3721fd9 100644 --- a/SharpCAT/Models/IRadio.cs +++ b/SharpCAT/Models/IRadio.cs @@ -1,7 +1,8 @@ using System; namespace SharpCATLib.Models { - public interface IRadio + partial interface IRadio { + } } diff --git a/SharpCAT/SharpCAT.cs b/SharpCAT/SharpCAT.cs index 231a8ae..ccad4b2 100644 --- a/SharpCAT/SharpCAT.cs +++ b/SharpCAT/SharpCAT.cs @@ -28,7 +28,7 @@ namespace SharpCATLib public string[] PortNames { get => SerialPort.GetPortNames(); } - public enum BaudRates : int { TwelveHundred = 1200, TwentyFourHundred = 2400, FourtyEightHUndred = 4800, NinteySixHundred = 9600, NineteenTwo = 19200, ThirtyEightFour = 38400 }; + public enum BaudRates : int { Twelve = 1200, TwentyFour = 2400, FourtyEight = 4800, NinteySix = 9600, NineteenTwo = 19200, ThirtyEightFour = 38400 }; public static int[] DataBits { get; } = new int[] { 7, 8 }; @@ -36,12 +36,12 @@ namespace SharpCATLib private string ConnectPorts(string[] portnames) { - List ports = new List(); + List ports = new List(); foreach (string port in portnames) { //Testing - ports.Add(new Serial("COM11", BaudRates.ThirtyEightFour, Parity.None, StopBits.Two, Handshake.None)); + ports.Add(new SerialPort(port, (int)BaudRates.ThirtyEightFour, Parity.None, (int)StopBits.Two, (int)Handshake.None)); } return ""; } diff --git a/SharpCAT/SharpCATLib.csproj b/SharpCAT/SharpCATLib.csproj index d0af5b1..0779341 100644 --- a/SharpCAT/SharpCATLib.csproj +++ b/SharpCAT/SharpCATLib.csproj @@ -4,10 +4,12 @@ netstandard2.0 - - - - + + default + + + default + PreserveNewest @@ -29,4 +31,7 @@ + + + diff --git a/SharpCAT/SharpCATLib.sln b/SharpCAT/SharpCATLib.sln new file mode 100644 index 0000000..b06762b --- /dev/null +++ b/SharpCAT/SharpCATLib.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.808.10 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpCATLib", "SharpCATLib.csproj", "{DC978DDF-22CD-4FF2-8312-354A6F1174F9}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {DC978DDF-22CD-4FF2-8312-354A6F1174F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DC978DDF-22CD-4FF2-8312-354A6F1174F9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DC978DDF-22CD-4FF2-8312-354A6F1174F9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DC978DDF-22CD-4FF2-8312-354A6F1174F9}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {1373D69F-5DEC-46AE-BC40-3D8B7A9D57BD} + EndGlobalSection +EndGlobal diff --git a/SharpCATConsole/App.config b/SharpCATConsole/App.config deleted file mode 100644 index 0f3b743..0000000 --- a/SharpCATConsole/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/SharpCATConsole/Program.cs b/SharpCATConsole/Program.cs deleted file mode 100644 index 7d755ae..0000000 --- a/SharpCATConsole/Program.cs +++ /dev/null @@ -1,26 +0,0 @@ -using SharpCATLib; -using System; - -namespace SharpCATConsole -{ - internal class Program - { - private static void Main(string[] args) - { - SharpCAT sharpCAT = new SharpCAT(); - - Console.WriteLine("Ports found: "); - foreach (var port in sharpCAT.PortNames) - { - Console.WriteLine(port); - } - - Console.ReadKey(); - } - - private static string SharpCAT_PortsSelected(string[] portnames) - { - throw new NotImplementedException(); - } - } -} \ No newline at end of file diff --git a/SharpCATConsole/Properties/AssemblyInfo.cs b/SharpCATConsole/Properties/AssemblyInfo.cs deleted file mode 100644 index 70dd7a1..0000000 --- a/SharpCATConsole/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("SharpCATConsole")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("SharpCATConsole")] -[assembly: AssemblyCopyright("Copyright © 2019")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("c0c9250e-efce-4f8f-8249-345647440eb9")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/SharpCATConsole/SharpCATConsole.csproj b/SharpCATConsole/SharpCATConsole.csproj deleted file mode 100644 index 7a15c4c..0000000 --- a/SharpCATConsole/SharpCATConsole.csproj +++ /dev/null @@ -1,63 +0,0 @@ - - - - - Debug - AnyCPU - {C0C9250E-EFCE-4F8F-8249-345647440EB9} - Exe - SharpCATConsole - SharpCATConsole - v4.7.2 - 512 - true - true - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - ..\packages\System.IO.Ports.4.5.0\lib\net461\System.IO.Ports.dll - - - - - - - - - - - - - - - - - - - {dad3e7be-905a-4768-a695-0bcf96171e35} - SharpCATLib - - - - \ No newline at end of file diff --git a/SharpCATService/App.config b/SharpCATService/App.config deleted file mode 100644 index 0f3b743..0000000 --- a/SharpCATService/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/SharpCATService/Client.cs b/SharpCATService/Client.cs deleted file mode 100644 index 6f1dd42..0000000 --- a/SharpCATService/Client.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace SharpCATService -{ - internal class Client - { - } -} \ No newline at end of file diff --git a/SharpCATService/Program.cs b/SharpCATService/Program.cs deleted file mode 100644 index b62f4c2..0000000 --- a/SharpCATService/Program.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.ServiceProcess; - -namespace SharpCATService -{ - internal static class Program - { - /// - /// The main entry point for the application. - /// - private static void Main() - { - ServiceBase[] ServicesToRun; - ServicesToRun = new ServiceBase[] - { - new Service() - }; - ServiceBase.Run(ServicesToRun); - } - } -} \ No newline at end of file diff --git a/SharpCATService/Properties/AssemblyInfo.cs b/SharpCATService/Properties/AssemblyInfo.cs deleted file mode 100644 index 30390dd..0000000 --- a/SharpCATService/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("SharpCATService")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("SharpCATService")] -[assembly: AssemblyCopyright("Copyright © 2019")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("8075c09f-03ee-40b2-a7df-22f39afcd40f")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/SharpCATService/Server.cs b/SharpCATService/Server.cs deleted file mode 100644 index 15c0c27..0000000 --- a/SharpCATService/Server.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace SharpCATService -{ - internal class Server - { - } -} \ No newline at end of file diff --git a/SharpCATService/Service.Designer.cs b/SharpCATService/Service.Designer.cs deleted file mode 100644 index 69e1b56..0000000 --- a/SharpCATService/Service.Designer.cs +++ /dev/null @@ -1,37 +0,0 @@ -namespace SharpCATService -{ - partial class Service - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Component Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - components = new System.ComponentModel.Container(); - this.ServiceName = "Service1"; - } - - #endregion - } -} diff --git a/SharpCATService/Service.cs b/SharpCATService/Service.cs deleted file mode 100644 index 0d2dc22..0000000 --- a/SharpCATService/Service.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.ServiceProcess; - -namespace SharpCATService -{ - public partial class Service : ServiceBase - { - public Service() - { - InitializeComponent(); - } - - protected override void OnStart(string[] args) - { - } - - protected override void OnStop() - { - } - } -} \ No newline at end of file diff --git a/SharpCATService/SharpCATService.csproj b/SharpCATService/SharpCATService.csproj deleted file mode 100644 index c60b5fd..0000000 --- a/SharpCATService/SharpCATService.csproj +++ /dev/null @@ -1,68 +0,0 @@ - - - - - Debug - AnyCPU - {8075C09F-03EE-40B2-A7DF-22F39AFCD40F} - WinExe - SharpCATService - SharpCATService - v4.7.2 - 512 - true - true - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - - - - - - - Component - - - Service.cs - - - - - - - - - - {dad3e7be-905a-4768-a695-0bcf96171e35} - SharpCATLib - - - - \ No newline at end of file diff --git a/VirtualSerial2/ComPort/VirtualSerial2um.vcxproj b/VirtualSerial2/ComPort/VirtualSerial2um.vcxproj new file mode 100644 index 0000000..e733174 --- /dev/null +++ b/VirtualSerial2/ComPort/VirtualSerial2um.vcxproj @@ -0,0 +1,187 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {65A037CB-9245-442A-A791-5CFC34E97BF7} + $(MSBuildProjectName) + 2 + Debug + Win32 + {D3E77C62-C6F0-4210-824E-1875C7B48EC5} + + + + Windows10 + False + Universal + UMDF + WindowsUserModeDriver10.0 + DynamicLibrary + + + Windows10 + True + Universal + UMDF + WindowsUserModeDriver10.0 + DynamicLibrary + + + Windows10 + False + Universal + UMDF + WindowsUserModeDriver10.0 + DynamicLibrary + + + Windows10 + True + Universal + UMDF + WindowsUserModeDriver10.0 + DynamicLibrary + + + + $(IntDir) + + + + + + + + + + + + + + + + VirtualSerial2um + + + VirtualSerial2um + + + VirtualSerial2um + + + VirtualSerial2um + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH)\wdm;..\..\inc + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH)\wdm;..\..\inc + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH)\wdm;..\..\inc + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH)\wdm;..\..\inc + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH)\wdm;..\..\inc + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH)\wdm;..\..\inc + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH)\wdm;..\..\inc + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH)\wdm;..\..\inc + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH)\wdm;..\..\inc + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH)\wdm;..\..\inc + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH)\wdm;..\..\inc + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH)\wdm;..\..\inc + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/VirtualSerial2/ComPort/VirtualSerial2um.vcxproj.Filters b/VirtualSerial2/ComPort/VirtualSerial2um.vcxproj.Filters new file mode 100644 index 0000000..7025cdf --- /dev/null +++ b/VirtualSerial2/ComPort/VirtualSerial2um.vcxproj.Filters @@ -0,0 +1,40 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {75562F82-D2DC-4C96-8D1E-01C195D61A2C} + + + h;hpp;hxx;hm;inl;inc;xsd + {0748F304-5FE6-4C01-B27A-3CD7B1A268B7} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {87F14B94-F7A4-42E8-9931-8ADDA3989EBD} + + + inf;inv;inx;mof;mc; + {30F678DB-C12C-461E-8128-CE409AAC9C42} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/VirtualSerial2/ComPort/virtualserial2um.inx b/VirtualSerial2/ComPort/virtualserial2um.inx new file mode 100644 index 0000000..6d95d79 Binary files /dev/null and b/VirtualSerial2/ComPort/virtualserial2um.inx differ diff --git a/VirtualSerial2/ComPort/virtualserial2um.rc b/VirtualSerial2/ComPort/virtualserial2um.rc new file mode 100644 index 0000000..f9f0a8d --- /dev/null +++ b/VirtualSerial2/ComPort/virtualserial2um.rc @@ -0,0 +1,17 @@ +//--------------------------------------------------------------------------- +// Virtualserial.rc +// +// Copyright (c) Microsoft Corporation, All Rights Reserved +//--------------------------------------------------------------------------- + + +#include +#include + +#define VER_FILETYPE VFT_DLL +#define VER_FILESUBTYPE VFT_UNKNOWN +#define VER_FILEDESCRIPTION_STR "WDF:UMDF VirtualSerial User-Mode v2 Driver Sample" +#define VER_INTERNALNAME_STR "VirtualSerial2um" +#define VER_ORIGINALFILENAME_STR "Virtualserial2um.dll" + +#include "common.ver" diff --git a/VirtualSerial2/FakeModem/fakemodem2um.inx b/VirtualSerial2/FakeModem/fakemodem2um.inx new file mode 100644 index 0000000..35bbc3c Binary files /dev/null and b/VirtualSerial2/FakeModem/fakemodem2um.inx differ diff --git a/VirtualSerial2/FakeModem/fakemodem2um.rc b/VirtualSerial2/FakeModem/fakemodem2um.rc new file mode 100644 index 0000000..7d916be --- /dev/null +++ b/VirtualSerial2/FakeModem/fakemodem2um.rc @@ -0,0 +1,17 @@ +//--------------------------------------------------------------------------- +// FakeModem.rc +// +// Copyright (c) Microsoft Corporation, All Rights Reserved +//--------------------------------------------------------------------------- + + +#include +#include + +#define VER_FILETYPE VFT_DLL +#define VER_FILESUBTYPE VFT_UNKNOWN +#define VER_FILEDESCRIPTION_STR "WDF:UMDF Fake Modem User-Mode Driver Sample" +#define VER_INTERNALNAME_STR "FakeModem2um" +#define VER_ORIGINALFILENAME_STR "FakeModem2um.dll" + +#include "common.ver" diff --git a/VirtualSerial2/FakeModem/fakemodem2um.vcxproj b/VirtualSerial2/FakeModem/fakemodem2um.vcxproj new file mode 100644 index 0000000..cb21918 --- /dev/null +++ b/VirtualSerial2/FakeModem/fakemodem2um.vcxproj @@ -0,0 +1,187 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {CD78D78F-B132-4E6F-A11F-B62185A6152A} + $(MSBuildProjectName) + 2 + Debug + Win32 + {3E00ED06-5DB5-444F-8FFA-D098A0218DF2} + + + + Windows10 + False + Universal + UMDF + WindowsUserModeDriver10.0 + DynamicLibrary + + + Windows10 + True + Universal + UMDF + WindowsUserModeDriver10.0 + DynamicLibrary + + + Windows10 + False + Universal + UMDF + WindowsUserModeDriver10.0 + DynamicLibrary + + + Windows10 + True + Universal + UMDF + WindowsUserModeDriver10.0 + DynamicLibrary + + + + $(IntDir) + + + + + + + + + + + + + + + + fakemodem2um + + + fakemodem2um + + + fakemodem2um + + + fakemodem2um + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE;_FAKE_MODEM=1 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH)\wdm;..\..\inc + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE;_FAKE_MODEM=1 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH)\wdm;..\..\inc + + + %(PreprocessorDefinitions);_UNICODE;UNICODE;_FAKE_MODEM=1 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH)\wdm;..\..\inc + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE;_FAKE_MODEM=1 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH)\wdm;..\..\inc + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE;_FAKE_MODEM=1 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH)\wdm;..\..\inc + + + %(PreprocessorDefinitions);_UNICODE;UNICODE;_FAKE_MODEM=1 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH)\wdm;..\..\inc + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE;_FAKE_MODEM=1 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH)\wdm;..\..\inc + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE;_FAKE_MODEM=1 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH)\wdm;..\..\inc + + + %(PreprocessorDefinitions);_UNICODE;UNICODE;_FAKE_MODEM=1 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH)\wdm;..\..\inc + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE;_FAKE_MODEM=1 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH)\wdm;..\..\inc + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE;_FAKE_MODEM=1 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH)\wdm;..\..\inc + + + %(PreprocessorDefinitions);_UNICODE;UNICODE;_FAKE_MODEM=1 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH)\wdm;..\..\inc + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/VirtualSerial2/FakeModem/fakemodem2um.vcxproj.Filters b/VirtualSerial2/FakeModem/fakemodem2um.vcxproj.Filters new file mode 100644 index 0000000..4d54300 --- /dev/null +++ b/VirtualSerial2/FakeModem/fakemodem2um.vcxproj.Filters @@ -0,0 +1,40 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {5A926099-4361-4BDC-BF0F-D98AD6F55C3A} + + + h;hpp;hxx;hm;inl;inc;xsd + {9C5FC831-BCE8-4E84-8BED-0B1182AA7358} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {43B83C13-0033-4A5E-A0DB-F0BA1B304FB0} + + + inf;inv;inx;mof;mc; + {83A3125A-7FA1-4416-B24A-FA9C1EECAAF0} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/VirtualSerial2/README.md b/VirtualSerial2/README.md new file mode 100644 index 0000000..9c743dc --- /dev/null +++ b/VirtualSerial2/README.md @@ -0,0 +1,64 @@ +--- +page_type: sample +urlFragment: virtual-serial-driver-sample-v2 +description: "Demonstrates UMDF version 2 serial drivers and includes a simple virtual serial driver (ComPort) and a controller-less modem driver (FakeModem)." +languages: +- cpp +products: +- windows +- windows-wdk +--- + +# Virtual serial driver sample (V2) + +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 the [Serial Controller Driver Design Guide](https://docs.microsoft.com/windows-hardware/drivers/serports/). + +## Code tour + +### comsup.cpp and 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 and 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 and driver.h + +- Definition and implementation of the device callback class (CMyDriver) for the sample. This includes events on the framework device object. + +### queue.cpp and 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/VirtualSerial2/VirtualSerial.sln b/VirtualSerial2/VirtualSerial.sln new file mode 100644 index 0000000..79d6c48 --- /dev/null +++ b/VirtualSerial2/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", "{F7F21610-DDE7-4709-9C48-68A0ABD1FF65}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "FakeModem", "FakeModem", "{704FA9C6-88FA-4381-8FA5-3407E62FAF20}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VirtualSerial2um", "ComPort\VirtualSerial2um.vcxproj", "{65A037CB-9245-442A-A791-5CFC34E97BF7}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fakemodem2um", "FakeModem\fakemodem2um.vcxproj", "{CD78D78F-B132-4E6F-A11F-B62185A6152A}" +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 + {65A037CB-9245-442A-A791-5CFC34E97BF7}.Debug|Win32.ActiveCfg = Debug|Win32 + {65A037CB-9245-442A-A791-5CFC34E97BF7}.Debug|Win32.Build.0 = Debug|Win32 + {65A037CB-9245-442A-A791-5CFC34E97BF7}.Release|Win32.ActiveCfg = Release|Win32 + {65A037CB-9245-442A-A791-5CFC34E97BF7}.Release|Win32.Build.0 = Release|Win32 + {65A037CB-9245-442A-A791-5CFC34E97BF7}.Debug|x64.ActiveCfg = Debug|x64 + {65A037CB-9245-442A-A791-5CFC34E97BF7}.Debug|x64.Build.0 = Debug|x64 + {65A037CB-9245-442A-A791-5CFC34E97BF7}.Release|x64.ActiveCfg = Release|x64 + {65A037CB-9245-442A-A791-5CFC34E97BF7}.Release|x64.Build.0 = Release|x64 + {CD78D78F-B132-4E6F-A11F-B62185A6152A}.Debug|Win32.ActiveCfg = Debug|Win32 + {CD78D78F-B132-4E6F-A11F-B62185A6152A}.Debug|Win32.Build.0 = Debug|Win32 + {CD78D78F-B132-4E6F-A11F-B62185A6152A}.Release|Win32.ActiveCfg = Release|Win32 + {CD78D78F-B132-4E6F-A11F-B62185A6152A}.Release|Win32.Build.0 = Release|Win32 + {CD78D78F-B132-4E6F-A11F-B62185A6152A}.Debug|x64.ActiveCfg = Debug|x64 + {CD78D78F-B132-4E6F-A11F-B62185A6152A}.Debug|x64.Build.0 = Debug|x64 + {CD78D78F-B132-4E6F-A11F-B62185A6152A}.Release|x64.ActiveCfg = Release|x64 + {CD78D78F-B132-4E6F-A11F-B62185A6152A}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {65A037CB-9245-442A-A791-5CFC34E97BF7} = {F7F21610-DDE7-4709-9C48-68A0ABD1FF65} + {CD78D78F-B132-4E6F-A11F-B62185A6152A} = {704FA9C6-88FA-4381-8FA5-3407E62FAF20} + EndGlobalSection +EndGlobal diff --git a/VirtualSerial2/device.c b/VirtualSerial2/device.c new file mode 100644 index 0000000..82f9f9a --- /dev/null +++ b/VirtualSerial2/device.c @@ -0,0 +1,449 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved. + +Module Name: + + Device.c + +Abstract: + + This module contains the implementation of the 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 Driver Framework + +--*/ + +#include "internal.h" + +NTSTATUS +DeviceCreate( + _In_ WDFDRIVER Driver, + _In_ PWDFDEVICE_INIT DeviceInit, + _Out_ PDEVICE_CONTEXT *DeviceContext + ) +/*++ + + 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 + +--*/ +{ + NTSTATUS status; + WDF_OBJECT_ATTRIBUTES deviceAttributes; + WDFDEVICE device; + PDEVICE_CONTEXT deviceContext; + UNREFERENCED_PARAMETER (Driver); + + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE( + &deviceAttributes, + DEVICE_CONTEXT); + + deviceAttributes.SynchronizationScope = WdfSynchronizationScopeDevice; + deviceAttributes.EvtCleanupCallback = EvtDeviceCleanup; + + status = WdfDeviceCreate(&DeviceInit, + &deviceAttributes, + &device); + if (!NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "Error: WdfDeviceCreate failed 0x%x", status); + return status; + } + + deviceContext = GetDeviceContext(device); + deviceContext->Device = device; + + *DeviceContext = deviceContext; + + return status; +} + + +NTSTATUS +DeviceConfigure( + _In_ PDEVICE_CONTEXT DeviceContext + ) +/*++ + + 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 + +--*/ +{ + NTSTATUS status; + WDFDEVICE device = DeviceContext->Device; + WDFKEY key; + LPGUID guid; + errno_t errorNo; + + DECLARE_CONST_UNICODE_STRING(portName, REG_VALUENAME_PORTNAME); + DECLARE_UNICODE_STRING_SIZE (comPort, 10); + DECLARE_UNICODE_STRING_SIZE (symbolicLinkName, SYMBOLIC_LINK_NAME_LENGTH); + +#ifdef _FAKE_MODEM + // + // Compiled as fake modem + // + guid = (LPGUID) &GUID_DEVINTERFACE_MODEM; +#else + // + // Compiled as virtual serial port + // + guid = (LPGUID) &GUID_DEVINTERFACE_COMPORT; +#endif + + // + // Create device interface + // + status = WdfDeviceCreateDeviceInterface( + device, + guid, + NULL); + if (!NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "Error: Cannot create device interface"); + goto Exit; + } + + // + // Read the COM port number from the registry, which has been automatically + // created by "MsPorts!PortsClassInstaller" if INF file says "Class=Ports" + // + status = WdfDeviceOpenRegistryKey( + device, + PLUGPLAY_REGKEY_DEVICE, + KEY_QUERY_VALUE, + WDF_NO_OBJECT_ATTRIBUTES, + &key); + if (!NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "Error: Failed to retrieve device hardware key root"); + goto Exit; + } + + status = WdfRegistryQueryUnicodeString( + key, + &portName, + NULL, + &comPort); + if (!NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "Error: Failed to read PortName"); + goto Exit; + } + + // + // Manually create the symbolic link name. Length is the length in + // bytes not including the NULL terminator. + // + // 6054 and 26035 are code analysis warnings that comPort.Buffer might + // not be NULL terminated, while we know that they are. + // + #pragma warning(suppress: 6054 26035) + symbolicLinkName.Length = (USHORT)((wcslen(comPort.Buffer) * sizeof(wchar_t)) + + sizeof(SYMBOLIC_LINK_NAME_PREFIX) - sizeof(UNICODE_NULL)); + + if (symbolicLinkName.Length >= symbolicLinkName.MaximumLength) { + + Trace(TRACE_LEVEL_ERROR, "Error: Buffer overflow when creating COM port name. Size" + " is %d, buffer length is %d", symbolicLinkName.Length, symbolicLinkName.MaximumLength); + status = STATUS_BUFFER_OVERFLOW; + goto Exit; + } + + errorNo = wcscpy_s(symbolicLinkName.Buffer, + SYMBOLIC_LINK_NAME_LENGTH, + SYMBOLIC_LINK_NAME_PREFIX); + + if (errorNo != 0) { + Trace(TRACE_LEVEL_ERROR, + "Failed to copy %ws to buffer with error %d", + SYMBOLIC_LINK_NAME_PREFIX, errorNo); + status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + errorNo = wcscat_s(symbolicLinkName.Buffer, + SYMBOLIC_LINK_NAME_LENGTH, + comPort.Buffer); + + if (errorNo != 0) { + Trace(TRACE_LEVEL_ERROR, + "Failed to copy %ws to buffer with error %d", + comPort.Buffer, errorNo); + status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + // + // Create symbolic link + // + status = WdfDeviceCreateSymbolicLink( + device, + &symbolicLinkName); + if (!NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "Error: Cannot create symbolic link %ws", symbolicLinkName.Buffer); + goto Exit; + } + + status = DeviceGetPdoName(DeviceContext); + if (!NT_SUCCESS(status)) { + goto Exit; + } + + status = DeviceWriteLegacyHardwareKey( + DeviceContext->PdoName, + comPort.Buffer, + DeviceContext->Device); + if (NT_SUCCESS(status)) { + DeviceContext->CreatedLegacyHardwareKey = TRUE; + } + + status = QueueCreate(DeviceContext); + if (!NT_SUCCESS(status)) { + goto Exit; + } + +Exit: + return status; +} + + +NTSTATUS +DeviceGetPdoName( + _In_ PDEVICE_CONTEXT DeviceContext + ) +{ + NTSTATUS status; + WDFDEVICE device = DeviceContext->Device; + WDF_OBJECT_ATTRIBUTES attributes; + WDFMEMORY memory; + + WDF_OBJECT_ATTRIBUTES_INIT(&attributes); + attributes.ParentObject = device; + + status = WdfDeviceAllocAndQueryProperty( + device, + DevicePropertyPhysicalDeviceObjectName, + NonPagedPoolNx, + &attributes, + &memory); + if (!NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "Error: Failed to query PDO name"); + goto Exit; + } + + DeviceContext->PdoName = (PWCHAR) WdfMemoryGetBuffer(memory, NULL); + Trace(TRACE_LEVEL_ERROR, + "PDO Name is %ws", DeviceContext->PdoName); + +Exit: + return status; +} + + +NTSTATUS +DeviceWriteLegacyHardwareKey( + _In_ PWSTR PdoName, + _In_ PWSTR ComPort, + _In_ WDFDEVICE Device + ) +{ + WDFKEY key = NULL; + NTSTATUS status; + UNICODE_STRING pdoString = {0}; + UNICODE_STRING comPort = {0}; + + DECLARE_CONST_UNICODE_STRING(deviceSubkey, SERIAL_DEVICE_MAP); + + RtlInitUnicodeString(&pdoString, PdoName); + RtlInitUnicodeString(&comPort, ComPort); + + status = WdfDeviceOpenDevicemapKey(Device, + &deviceSubkey, + KEY_SET_VALUE, + WDF_NO_OBJECT_ATTRIBUTES, + &key); + if (!NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "Error: Failed to open DEVICEMAP\\SERIALCOMM key 0x%x", status); + goto exit; + } + + status = WdfRegistryAssignUnicodeString(key, + &pdoString, + &comPort); + + if (!NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "Error: Failed to write to DEVICEMAP\\SERIALCOMM key 0x%x", status); + goto exit; + } + +exit: + + if (key != NULL) { + WdfRegistryClose(key); + key = NULL; + } + + return status; +} + + +VOID +EvtDeviceCleanup( + _In_ WDFOBJECT Object + ) +{ + WDFDEVICE device = (WDFDEVICE) Object; + PDEVICE_CONTEXT deviceContext = GetDeviceContext(device); + NTSTATUS status; + WDFKEY key = NULL; + UNICODE_STRING pdoString = {0}; + + DECLARE_CONST_UNICODE_STRING(deviceSubkey, SERIAL_DEVICE_MAP); + + if (deviceContext->CreatedLegacyHardwareKey == TRUE) { + + RtlInitUnicodeString(&pdoString, deviceContext->PdoName); + + status = WdfDeviceOpenDevicemapKey(device, + &deviceSubkey, + KEY_SET_VALUE, + WDF_NO_OBJECT_ATTRIBUTES, + &key); + + if (!NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "Error: Failed to open DEVICEMAP\\SERIALCOMM key 0x%x", status); + goto exit; + } + + status = WdfRegistryRemoveValue(key, + &pdoString); + if (!NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "Error: Failed to delete %S key, 0x%x", pdoString.Buffer, status); + goto exit; + } + + status = WdfRegistryRemoveKey(key); + if (!NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "Error: Failed to delete %S, 0x%x", SERIAL_DEVICE_MAP, status); + goto exit; + } + } + +exit: + + if (key != NULL) { + WdfRegistryClose(key); + key = NULL; + } + + return; +} + + +ULONG +GetBaudRate( + _In_ PDEVICE_CONTEXT DeviceContext + ) +{ + return DeviceContext->BaudRate; +} + +VOID +SetBaudRate( + _In_ PDEVICE_CONTEXT DeviceContext, + _In_ ULONG BaudRate + ) +{ + DeviceContext->BaudRate = BaudRate; +} + +ULONG * +GetModemControlRegisterPtr( + _In_ PDEVICE_CONTEXT DeviceContext + ) +{ + return &DeviceContext->ModemControlRegister; +} + +ULONG * +GetFifoControlRegisterPtr( + _In_ PDEVICE_CONTEXT DeviceContext + ) +{ + return &DeviceContext->FifoControlRegister; +} + +ULONG * +GetLineControlRegisterPtr( + _In_ PDEVICE_CONTEXT DeviceContext + ) +{ + return &DeviceContext->LineControlRegister; +} + +VOID +SetValidDataMask( + _In_ PDEVICE_CONTEXT DeviceContext, + _In_ UCHAR Mask + ) +{ + DeviceContext->ValidDataMask = Mask; +} + +VOID +SetTimeouts( + _In_ PDEVICE_CONTEXT DeviceContext, + _In_ SERIAL_TIMEOUTS Timeouts + ) +{ + DeviceContext->Timeouts = Timeouts; +} + +VOID +GetTimeouts( + _In_ PDEVICE_CONTEXT DeviceContext, + _Out_ SERIAL_TIMEOUTS *Timeouts + ) +{ + *Timeouts = DeviceContext->Timeouts; +} diff --git a/VirtualSerial2/device.h b/VirtualSerial2/device.h new file mode 100644 index 0000000..e271884 --- /dev/null +++ b/VirtualSerial2/device.h @@ -0,0 +1,122 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved + +Module Name: + + Device.h + +Abstract: + + This module contains the type definitions for the VirtualSerial sample + driver's device callback class. + +Environment: + + Windows Driver Framework + +--*/ + +#pragma once + +#define SYMBOLIC_LINK_NAME_LENGTH 32 +#define SYMBOLIC_LINK_NAME_PREFIX L"\\DosDevices\\Global\\" +#define REG_PATH_DEVICEMAP L"HARDWARE\\DEVICEMAP" +#define SERIAL_DEVICE_MAP L"SERIALCOMM" +#define REG_VALUENAME_PORTNAME L"PortName" +#define REG_PATH_SERIALCOMM REG_PATH_DEVICEMAP L"\\" SERIAL_DEVICE_MAP + +typedef struct _DEVICE_CONTEXT +{ + WDFDEVICE Device; + + ULONG BaudRate; + + ULONG ModemControlRegister; + + ULONG FifoControlRegister; + + ULONG LineControlRegister; + + UCHAR ValidDataMask; + + SERIAL_TIMEOUTS Timeouts; + + BOOLEAN CreatedLegacyHardwareKey; + + PWSTR PdoName; + +} DEVICE_CONTEXT, *PDEVICE_CONTEXT; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(DEVICE_CONTEXT, GetDeviceContext); + + +NTSTATUS +DeviceCreate( + _In_ WDFDRIVER Driver, + _In_ PWDFDEVICE_INIT DeviceInit, + _Out_ PDEVICE_CONTEXT *DeviceContext + ); + +NTSTATUS +DeviceConfigure( + _In_ PDEVICE_CONTEXT DeviceContext + ); + +NTSTATUS +DeviceGetPdoName( + _In_ PDEVICE_CONTEXT DeviceContext + ); + +NTSTATUS +DeviceWriteLegacyHardwareKey( + _In_ PWSTR PdoName, + _In_ PWSTR ComPort, + _In_ WDFDEVICE Device + ); + +EVT_WDF_DEVICE_CONTEXT_CLEANUP EvtDeviceCleanup; + +ULONG +GetBaudRate( + _In_ PDEVICE_CONTEXT DeviceContext + ); + +VOID +SetBaudRate( + _In_ PDEVICE_CONTEXT DeviceContext, + _In_ ULONG BaudRate + ); + +ULONG * +GetModemControlRegisterPtr( + _In_ PDEVICE_CONTEXT DeviceContext + ); + +ULONG * +GetFifoControlRegisterPtr( + _In_ PDEVICE_CONTEXT DeviceContext + ); + +ULONG * +GetLineControlRegisterPtr( + _In_ PDEVICE_CONTEXT DeviceContext + ); + +VOID +SetValidDataMask( + _In_ PDEVICE_CONTEXT DeviceContext, + _In_ UCHAR Mask + ); + +VOID +SetTimeouts( + _In_ PDEVICE_CONTEXT DeviceContext, + _In_ SERIAL_TIMEOUTS Timeouts + ); + +VOID +GetTimeouts( + _In_ PDEVICE_CONTEXT DeviceContext, + _Out_ SERIAL_TIMEOUTS *Timeouts + ); \ No newline at end of file diff --git a/VirtualSerial2/driver.c b/VirtualSerial2/driver.c new file mode 100644 index 0000000..bb7f4c7 --- /dev/null +++ b/VirtualSerial2/driver.c @@ -0,0 +1,72 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved. + +Module Name: + + Driver.c + +Abstract: + + This module contains the implementation of the VirtualSerial Sample's + core driver callback object. + +Environment: + + Windows Driver Framework + +--*/ + +#include +#include "internal.h" + +NTSTATUS +DriverEntry( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ) +{ + NTSTATUS status; + WDF_DRIVER_CONFIG driverConfig; + + WDF_DRIVER_CONFIG_INIT(&driverConfig, + EvtDeviceAdd); + + status = WdfDriverCreate(DriverObject, + RegistryPath, + WDF_NO_OBJECT_ATTRIBUTES, + &driverConfig, + WDF_NO_HANDLE); + if (!NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "Error: WdfDriverCreate failed 0x%x", status); + return status; + } + + return status; +} + +NTSTATUS +EvtDeviceAdd( + _In_ WDFDRIVER Driver, + _Inout_ PWDFDEVICE_INIT DeviceInit + ) +{ + NTSTATUS status; + PDEVICE_CONTEXT deviceContext; + + status = DeviceCreate(Driver, + DeviceInit, + &deviceContext); + if (!NT_SUCCESS(status)) { + return status; + } + + status = DeviceConfigure(deviceContext); + if (!NT_SUCCESS(status)) { + return status; + } + + return status; +} + diff --git a/VirtualSerial2/driver.h b/VirtualSerial2/driver.h new file mode 100644 index 0000000..787cef0 --- /dev/null +++ b/VirtualSerial2/driver.h @@ -0,0 +1,30 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved + +Module Name: + + Driver.h + +Abstract: + + This module contains the type definitions for the VirtualSerial sample's + driver callback class. + +Environment: + + Windows Driver Framework + +--*/ + +#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. +// + +DRIVER_INITIALIZE DriverEntry; + +EVT_WDF_DRIVER_DEVICE_ADD EvtDeviceAdd; diff --git a/VirtualSerial2/internal.h b/VirtualSerial2/internal.h new file mode 100644 index 0000000..82c149e --- /dev/null +++ b/VirtualSerial2/internal.h @@ -0,0 +1,58 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved + +Module Name: + + Internal.h + +Abstract: + + This module contains the local type definitions for the VirtualSerial + driver sample. + +Environment: + + Windows Driver Framework + +--*/ + +#pragma once + +#ifdef _KERNEL_MODE +#include +#else +#include +#endif + +#include + +#define _NTDEF_ + +// +// Include the type specific headers. +// +#include "serial.h" +#include "driver.h" +#include "device.h" +#include "ringbuffer.h" +#include "queue.h" + +// +// Tracing and Assert +// + +#define Trace(level, _fmt_, ...) \ + DbgPrintEx(DPFLTR_DEFAULT_ID, level, \ + _fmt_ "\n", __VA_ARGS__) + +#define TRACE_LEVEL_ERROR DPFLTR_ERROR_LEVEL +#define TRACE_LEVEL_INFO DPFLTR_INFO_LEVEL + +#ifndef ASSERT +#define ASSERT(exp) { \ + if (!(exp)) { \ + RtlAssert(#exp, __FILE__, __LINE__, NULL); \ + } \ +} +#endif \ No newline at end of file diff --git a/VirtualSerial2/queue.c b/VirtualSerial2/queue.c new file mode 100644 index 0000000..80571b4 --- /dev/null +++ b/VirtualSerial2/queue.c @@ -0,0 +1,963 @@ +/*++ + +Copyright (c) Microsoft Corporation, All Rights Reserved + +Module Name: + + Queue.c + +Abstract: + + This file implements the I/O queue interface and performs + the read/write/ioctl operations. + +Environment: + + Windows Driver Framework + +--*/ + + +#include "internal.h" + +NTSTATUS +QueueCreate( + _In_ PDEVICE_CONTEXT DeviceContext + ) +{ + NTSTATUS status; + WDFDEVICE device = DeviceContext->Device; + WDF_IO_QUEUE_CONFIG queueConfig; + WDF_OBJECT_ATTRIBUTES queueAttributes; + WDFQUEUE queue; + PQUEUE_CONTEXT queueContext; + + // + // Create the default queue + // + + WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE( + &queueConfig, + WdfIoQueueDispatchParallel); + + queueConfig.EvtIoRead = EvtIoRead; + queueConfig.EvtIoWrite = EvtIoWrite; + queueConfig.EvtIoDeviceControl = EvtIoDeviceControl; + + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE( + &queueAttributes, + QUEUE_CONTEXT); + + status = WdfIoQueueCreate( + device, + &queueConfig, + &queueAttributes, + &queue); + + if( !NT_SUCCESS(status) ) { + Trace(TRACE_LEVEL_ERROR, + "Error: WdfIoQueueCreate failed 0x%x", status); + return status; + } + + queueContext = GetQueueContext(queue); + queueContext->Queue = queue; + queueContext->DeviceContext = DeviceContext; + + // + // Create a manual queue to hold pending read requests. By keeping + // them in the queue, framework takes care of cancelling them if the app + // exits + // + + WDF_IO_QUEUE_CONFIG_INIT( + &queueConfig, + WdfIoQueueDispatchManual); + + status = WdfIoQueueCreate( + device, + &queueConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &queue); + + if( !NT_SUCCESS(status) ) { + Trace(TRACE_LEVEL_ERROR, + "Error: WdfIoQueueCreate manual queue failed 0x%x", status); + return status; + } + + queueContext->ReadQueue = queue; + + // + // Create another manual queue to hold pending IOCTL_SERIAL_WAIT_ON_MASK + // + + WDF_IO_QUEUE_CONFIG_INIT( + &queueConfig, + WdfIoQueueDispatchManual); + + status = WdfIoQueueCreate( + device, + &queueConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &queue); + + if( !NT_SUCCESS(status) ) { + Trace(TRACE_LEVEL_ERROR, + "Error: WdfIoQueueCreate manual queue failed 0x%x", status); + return status; + } + + queueContext->WaitMaskQueue = queue; + + RingBufferInitialize(&queueContext->RingBuffer, + queueContext->Buffer, + sizeof(queueContext->Buffer)); + + return status; +} + + +NTSTATUS +RequestCopyFromBuffer( + _In_ WDFREQUEST Request, + _In_ PVOID SourceBuffer, + _In_ size_t NumBytesToCopyFrom + ) +{ + NTSTATUS status; + WDFMEMORY memory; + + status = WdfRequestRetrieveOutputMemory(Request, &memory); + if( !NT_SUCCESS(status) ) { + Trace(TRACE_LEVEL_ERROR, + "Error: WdfRequestRetrieveOutputMemory failed 0x%x", status); + return status; + } + + status = WdfMemoryCopyFromBuffer(memory, 0, + SourceBuffer, NumBytesToCopyFrom); + if( !NT_SUCCESS(status) ) { + Trace(TRACE_LEVEL_ERROR, + "Error: WdfMemoryCopyFromBuffer failed 0x%x", status); + return status; + } + + WdfRequestSetInformation(Request, NumBytesToCopyFrom); + return status; +} + + +NTSTATUS +RequestCopyToBuffer( + _In_ WDFREQUEST Request, + _In_ PVOID DestinationBuffer, + _In_ size_t NumBytesToCopyTo + ) +{ + NTSTATUS status; + WDFMEMORY memory; + + status = WdfRequestRetrieveInputMemory(Request, &memory); + if( !NT_SUCCESS(status) ) { + Trace(TRACE_LEVEL_ERROR, + "Error: WdfRequestRetrieveInputMemory failed 0x%x", status); + return status; + } + + status = WdfMemoryCopyToBuffer(memory, 0, + DestinationBuffer, NumBytesToCopyTo); + if( !NT_SUCCESS(status) ) { + Trace(TRACE_LEVEL_ERROR, + "Error: WdfMemoryCopyToBuffer failed 0x%x", status); + return status; + } + + WdfRequestSetInformation(Request, NumBytesToCopyTo); + return status; +} + + +VOID +EvtIoDeviceControl( + _In_ WDFQUEUE Queue, + _In_ WDFREQUEST Request, + _In_ size_t OutputBufferLength, + _In_ size_t InputBufferLength, + _In_ ULONG IoControlCode + ) +{ + NTSTATUS status; + PQUEUE_CONTEXT queueContext = GetQueueContext(Queue); + PDEVICE_CONTEXT deviceContext = queueContext->DeviceContext; + UNREFERENCED_PARAMETER (OutputBufferLength); + UNREFERENCED_PARAMETER (InputBufferLength); + + Trace(TRACE_LEVEL_INFO, + "EvtIoDeviceControl 0x%x", IoControlCode); + + switch (IoControlCode) + { + + 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}; + + status = RequestCopyToBuffer(Request, + &baudRateBuffer, + sizeof(baudRateBuffer)); + + if( NT_SUCCESS(status) ) { + SetBaudRate(deviceContext, baudRateBuffer.BaudRate); + }; + break; + } + + case IOCTL_SERIAL_GET_BAUD_RATE: + { + SERIAL_BAUD_RATE baudRateBuffer = {0}; + + baudRateBuffer.BaudRate = GetBaudRate(deviceContext); + + status = RequestCopyFromBuffer(Request, + &baudRateBuffer, + sizeof(baudRateBuffer)); + 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 *modemControlRegister = GetModemControlRegisterPtr(deviceContext); + + ASSERT(modemControlRegister); + + status = RequestCopyToBuffer(Request, + modemControlRegister, + sizeof(ULONG)); + break; + } + + case IOCTL_SERIAL_GET_MODEM_CONTROL: + { + ULONG *modemControlRegister = GetModemControlRegisterPtr(deviceContext); + + ASSERT(modemControlRegister); + + status = RequestCopyFromBuffer(Request, + modemControlRegister, + 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 *fifoControlRegister = GetFifoControlRegisterPtr(deviceContext); + + ASSERT(fifoControlRegister); + + status = RequestCopyToBuffer(Request, + fifoControlRegister, + sizeof(ULONG)); + break; + } + + case IOCTL_SERIAL_GET_LINE_CONTROL: + { + status = QueueProcessGetLineControl( + queueContext, + Request); + break; + } + + + case IOCTL_SERIAL_SET_LINE_CONTROL: + { + status = QueueProcessSetLineControl( + queueContext, + Request); + break; + } + + case IOCTL_SERIAL_GET_TIMEOUTS: + { + SERIAL_TIMEOUTS timeoutValues = {0}; + + status = RequestCopyFromBuffer(Request, + (void*) &timeoutValues, + sizeof(timeoutValues)); + break; + } + + case IOCTL_SERIAL_SET_TIMEOUTS: + { + SERIAL_TIMEOUTS timeoutValues = {0}; + + status = RequestCopyToBuffer(Request, + (void*) &timeoutValues, + sizeof(timeoutValues)); + + if( NT_SUCCESS(status) ) + { + if ((timeoutValues.ReadIntervalTimeout == MAXULONG) && + (timeoutValues.ReadTotalTimeoutMultiplier == MAXULONG) && + (timeoutValues.ReadTotalTimeoutConstant == MAXULONG)) + { + status = STATUS_INVALID_PARAMETER; + } + } + + if( NT_SUCCESS(status) ) { + SetTimeouts(deviceContext, timeoutValues); + } + + break; + } + + case IOCTL_SERIAL_WAIT_ON_MASK: + { + // + // NOTE: A wait-on-mask request should not be completed until either: + // 1) A wait event occurs; or + // 2) A set-wait-mask request is received + // + // This is a driver for a virtual serial port. Since there is no + // actual hardware, we complete the request with some failure code. + // + WDFREQUEST savedRequest; + + status = WdfIoQueueRetrieveNextRequest( + queueContext->WaitMaskQueue, + &savedRequest); + + if (NT_SUCCESS(status)) { + WdfRequestComplete(savedRequest, + STATUS_UNSUCCESSFUL); + } + + // + // Keep the request in a manual queue and the framework will take + // care of cancelling them when the app exits + // + status = WdfRequestForwardToIoQueue( + Request, + queueContext->WaitMaskQueue); + + if( !NT_SUCCESS(status) ) { + Trace(TRACE_LEVEL_ERROR, + "Error: WdfRequestForwardToIoQueue failed 0x%x", status); + WdfRequestComplete(Request, status); + } + + // + // Instead of "break", use "return" to prevent the current request + // from being completed. + // + return; + } + + case IOCTL_SERIAL_SET_WAIT_MASK: + { + // + // NOTE: If a wait-on-mask request is already pending when set-wait-mask + // request is processed, the pending wait-on-event request is completed + // with STATUS_SUCCESS and the output wait event mask is set to zero. + // + WDFREQUEST savedRequest; + + status = WdfIoQueueRetrieveNextRequest( + queueContext->WaitMaskQueue, + &savedRequest); + + if (NT_SUCCESS(status)) { + + ULONG eventMask = 0; + status = RequestCopyFromBuffer( + savedRequest, + &eventMask, + sizeof(eventMask)); + + WdfRequestComplete(savedRequest, status); + } + + // + // NOTE: The application expects STATUS_SUCCESS for these IOCTLs. + // + status = STATUS_SUCCESS; + 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. + // + status = STATUS_SUCCESS; + break; + + default: + status = STATUS_INVALID_PARAMETER; + break; + } + + // + // complete the request + // + WdfRequestComplete(Request, status); +} + + +VOID +EvtIoWrite( + _In_ WDFQUEUE Queue, + _In_ WDFREQUEST Request, + _In_ size_t Length + ) +{ + NTSTATUS status; + PQUEUE_CONTEXT queueContext = GetQueueContext(Queue); + WDFMEMORY memory; + WDFREQUEST savedRequest; + size_t availableData = 0; + + Trace(TRACE_LEVEL_INFO, + "EvtIoWrite 0x%p", Request); + + status = WdfRequestRetrieveInputMemory(Request, &memory); + if( !NT_SUCCESS(status) ) { + Trace(TRACE_LEVEL_ERROR, + "Error: WdfRequestRetrieveInputMemory failed 0x%x", status); + return; + } + + // + // Process input + // + status = QueueProcessWriteBytes( + queueContext, + (PUCHAR)WdfMemoryGetBuffer(memory, NULL), + Length); + if( !NT_SUCCESS(status) ) { + return; + } + + WdfRequestCompleteWithInformation(Request, status, Length); + + // + // Get the amount of data available in the ring buffer + // + RingBufferGetAvailableData( + &queueContext->RingBuffer, + &availableData); + + if (availableData == 0) { + return; + } + + // + // Continue with the next request, if there is one pending + // + for ( ; ; ) { + + status = WdfIoQueueRetrieveNextRequest( + queueContext->ReadQueue, + &savedRequest); + + if (!NT_SUCCESS(status)) { + break; + } + + status = WdfRequestForwardToIoQueue( + savedRequest, + Queue); + + if( !NT_SUCCESS(status) ) { + Trace(TRACE_LEVEL_ERROR, + "Error: WdfRequestForwardToIoQueue failed 0x%x", status); + WdfRequestComplete(savedRequest, status); + } + } +} + + +VOID +EvtIoRead( + _In_ WDFQUEUE Queue, + _In_ WDFREQUEST Request, + _In_ size_t Length + ) +{ + NTSTATUS status; + PQUEUE_CONTEXT queueContext = GetQueueContext(Queue); + WDFMEMORY memory; + size_t bytesCopied = 0; + + Trace(TRACE_LEVEL_INFO, + "EvtIoRead 0x%p", Request); + + status = WdfRequestRetrieveOutputMemory(Request, &memory); + if( !NT_SUCCESS(status) ) { + Trace(TRACE_LEVEL_ERROR, + "Error: WdfRequestRetrieveOutputMemory failed 0x%x", status); + WdfRequestComplete(Request, status); + return; + } + + status = RingBufferRead(&queueContext->RingBuffer, + (BYTE*)WdfMemoryGetBuffer(memory, NULL), + Length, + &bytesCopied); + if( !NT_SUCCESS(status) ) { + WdfRequestComplete(Request, status); + return; + } + + if (bytesCopied > 0) { + // + // Data was read from buffer succesfully + // + WdfRequestCompleteWithInformation(Request, status, bytesCopied); + return; + } + else { + // + // No data to read. Queue the request for later processing. + // + status = WdfRequestForwardToIoQueue(Request, + queueContext->ReadQueue); + if( !NT_SUCCESS(status) ) { + Trace(TRACE_LEVEL_ERROR, + "Error: WdfRequestForwardToIoQueue failed 0x%x", status); + WdfRequestComplete(Request, status); + } + } +} + + +NTSTATUS +QueueProcessWriteBytes( + _In_ PQUEUE_CONTEXT QueueContext, + _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 -- 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. +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + 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; + } + + status = RingBufferWrite(&QueueContext->RingBuffer, + ¤tCharacter, + sizeof(currentCharacter)); + if( !NT_SUCCESS(status) ) { + return status; + } + + switch (QueueContext->CommandMatchState) { + + case COMMAND_MATCH_STATE_IDLE: + + if ((currentCharacter == 'a') || (currentCharacter == 'A')) { + // + // got an A + // + QueueContext->CommandMatchState = COMMAND_MATCH_STATE_GOT_A; + QueueContext->ConnectCommand = FALSE; + QueueContext->IgnoreNextChar = FALSE; + } + break; + + case COMMAND_MATCH_STATE_GOT_A: + + if ((currentCharacter == 't') || (currentCharacter == 'T')) { + // + // got a T + // + QueueContext->CommandMatchState = COMMAND_MATCH_STATE_GOT_T; + } + else { + QueueContext->CommandMatchState = COMMAND_MATCH_STATE_IDLE; + } + + break; + + case COMMAND_MATCH_STATE_GOT_T: + + if (! QueueContext->IgnoreNextChar) { + // + // the last char was not a special char + // check for CONNECT command + // + if ((currentCharacter == 'A') || (currentCharacter == 'a')) { + QueueContext->ConnectCommand = TRUE; + } + + if ((currentCharacter == 'D') || (currentCharacter == 'd')) { + QueueContext->ConnectCommand = TRUE; + } + } + + QueueContext->IgnoreNextChar = TRUE; + + if (currentCharacter == '\r') { + // + // got a CR, send a response to the command + // + QueueContext->CommandMatchState = COMMAND_MATCH_STATE_IDLE; + + if (QueueContext->ConnectCommand) { + // + // place CONNECT in the buffer + // + status = RingBufferWrite(&QueueContext->RingBuffer, + connectString, + connectStringCch); + if( !NT_SUCCESS(status) ) { + return status; + } + // + // connected now raise CD + // + QueueContext->CurrentlyConnected = TRUE; + QueueContext->ConnectionStateChanged = TRUE; + } + else { + // + // place OK in the buffer + // + status = RingBufferWrite(&QueueContext->RingBuffer, + okString, + okStringCch); + if( !NT_SUCCESS(status) ) { + return status; + } + } + } + break; + + default: + break; + } + } + return status; +} + + +NTSTATUS +QueueProcessGetLineControl( + _In_ PQUEUE_CONTEXT QueueContext, + _In_ WDFREQUEST Request + ) +{ + NTSTATUS status; + PDEVICE_CONTEXT deviceContext; + SERIAL_LINE_CONTROL lineControl = {0}; + ULONG lineControlSnapshot; + ULONG *lineControlRegister; + + deviceContext = QueueContext->DeviceContext; + lineControlRegister = GetLineControlRegisterPtr(deviceContext); + + ASSERT(lineControlRegister); + + // + // Take a snapshot of the line control register variable + // + lineControlSnapshot = *lineControlRegister; + + // + // 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 + // + status = RequestCopyFromBuffer(Request, + (void*) &lineControl, + sizeof(lineControl)); + return status; +} + + +NTSTATUS +QueueProcessSetLineControl( + _In_ PQUEUE_CONTEXT QueueContext, + _In_ WDFREQUEST Request + ) +{ + NTSTATUS status; + PDEVICE_CONTEXT deviceContext; + SERIAL_LINE_CONTROL lineControl = {0}; + ULONG *lineControlRegister; + UCHAR lineControlData = 0; + UCHAR lineControlStop = 0; + UCHAR lineControlParity = 0; + ULONG lineControlSnapshot; + ULONG lineControlNew; + ULONG lineControlPrevious; + ULONG i; + + deviceContext = QueueContext->DeviceContext; + lineControlRegister = GetLineControlRegisterPtr(deviceContext); + + ASSERT(lineControlRegister); + + // + // 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. + // + status = RequestCopyToBuffer(Request, + (void*) &lineControl, + sizeof(lineControl)); + + // + // Bits 0 and 1 of the line control register + // + if( NT_SUCCESS(status) ) + { + switch (lineControl.WordLength) + { + case 5: + lineControlData = SERIAL_5_DATA; + SetValidDataMask(deviceContext, 0x1f); + break; + + case 6: + lineControlData = SERIAL_6_DATA; + SetValidDataMask(deviceContext, 0x3f); + break; + + case 7: + lineControlData = SERIAL_7_DATA; + SetValidDataMask(deviceContext, 0x7f); + break; + + case 8: + lineControlData = SERIAL_8_DATA; + SetValidDataMask(deviceContext, 0xff); + break; + + default: + status = STATUS_INVALID_PARAMETER; + break; + } + } + + // + // Bit 2 of the line control register + // + if( NT_SUCCESS(status) ) + { + switch (lineControl.StopBits) + { + case STOP_BIT_1: + lineControlStop = SERIAL_1_STOP; + break; + + case STOP_BITS_1_5: + if (lineControlData != SERIAL_5_DATA) + { + status = STATUS_INVALID_PARAMETER; + break; + } + lineControlStop = SERIAL_1_5_STOP; + break; + + case STOP_BITS_2: + if (lineControlData == SERIAL_5_DATA) + { + status = STATUS_INVALID_PARAMETER; + break; + } + lineControlStop = SERIAL_2_STOP; + break; + + default: + status = STATUS_INVALID_PARAMETER; + break; + } + } + + // + // Bits 3, 4 and 5 of the line control register + // + if( NT_SUCCESS(status) ) + { + 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: + status = STATUS_INVALID_PARAMETER; + break; + } + } + + // + // 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. + // +#ifdef _KERNEL_MODE + LARGE_INTEGER interval; + interval.QuadPart = 0; + KeDelayExecutionThread(UserMode, FALSE, &interval); +#else + SwitchToThread(); +#endif + } + + lineControlSnapshot = *lineControlRegister; + + lineControlNew = (lineControlSnapshot & SERIAL_LCR_BREAK) | + (lineControlData | lineControlParity | lineControlStop); + + lineControlPrevious = InterlockedCompareExchange( + (LONG *) lineControlRegister, + lineControlNew, + lineControlSnapshot); + + } while (lineControlPrevious != lineControlSnapshot); + + return status; +} diff --git a/VirtualSerial2/queue.h b/VirtualSerial2/queue.h new file mode 100644 index 0000000..909d699 --- /dev/null +++ b/VirtualSerial2/queue.h @@ -0,0 +1,113 @@ +/*++ + +Copyright (c) Microsoft Corporation, All Rights Reserved + +Module Name: + + queue.h + +Abstract: + + This file defines the queue callback interface. + +Environment: + + Windows Driver Framework + +--*/ + +#pragma once + +#include "internal.h" + +// Set ring buffer size +#define DATA_BUFFER_SIZE 1024 + +// +// Device states +// +#define COMMAND_MATCH_STATE_IDLE 0 +#define COMMAND_MATCH_STATE_GOT_A 1 +#define COMMAND_MATCH_STATE_GOT_T 2 + +// +// Define useful macros +// + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) +#endif + +#define MAXULONG 0xffffffff + +typedef struct _QUEUE_CONTEXT +{ + UCHAR CommandMatchState; + + BOOLEAN ConnectCommand; + + BOOLEAN IgnoreNextChar; + + BOOLEAN ConnectionStateChanged; + + BOOLEAN CurrentlyConnected; + + RING_BUFFER RingBuffer; // Ring buffer for pending data + + BYTE Buffer[DATA_BUFFER_SIZE]; + + WDFQUEUE Queue; // Default parallel queue + + WDFQUEUE ReadQueue; // Manual queue for pending reads + + WDFQUEUE WaitMaskQueue; // Manual queue for pending ioctl wait-on-mask + + PDEVICE_CONTEXT DeviceContext; + +} QUEUE_CONTEXT, *PQUEUE_CONTEXT; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(QUEUE_CONTEXT, GetQueueContext); + +EVT_WDF_IO_QUEUE_IO_READ EvtIoRead; +EVT_WDF_IO_QUEUE_IO_WRITE EvtIoWrite; +EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL EvtIoDeviceControl; + +NTSTATUS +QueueCreate( + _In_ PDEVICE_CONTEXT DeviceContext + ); + +NTSTATUS +QueueProcessWriteBytes( + _In_ PQUEUE_CONTEXT QueueContext, + _In_reads_bytes_(Length) + PUCHAR Characters, + _In_ size_t Length + ); + +NTSTATUS +QueueProcessGetLineControl( + _In_ PQUEUE_CONTEXT QueueContext, + _In_ WDFREQUEST Request + ); + +NTSTATUS +QueueProcessSetLineControl( + _In_ PQUEUE_CONTEXT QueueContext, + _In_ WDFREQUEST Request + ); + +NTSTATUS +RequestCopyFromBuffer( + _In_ WDFREQUEST Request, + _In_ PVOID SourceBuffer, + _In_ size_t NumBytesToCopyFrom + ); + +NTSTATUS +RequestCopyToBuffer( + _In_ WDFREQUEST Request, + _In_ PVOID DestinationBuffer, + _In_ size_t NumBytesToCopyTo + ); + diff --git a/VirtualSerial2/ringbuffer.c b/VirtualSerial2/ringbuffer.c new file mode 100644 index 0000000..0efbe82 --- /dev/null +++ b/VirtualSerial2/ringbuffer.c @@ -0,0 +1,338 @@ +/*++ + +Copyright (c) Microsoft Corporation, All Rights Reserved + +Module Name: + + RingBuffer.c + +Abstract: + + This file implements the Ring Buffer + +Environment: + +--*/ + +#include "internal.h" + +VOID +RingBufferInitialize( + _In_ PRING_BUFFER Self, + _In_reads_bytes_(BufferSize) + BYTE* Buffer, + _In_ size_t BufferSize + ) +{ + Self->Size = BufferSize; + Self->Base = Buffer; + Self->End = Buffer + BufferSize; + Self->Head = Buffer; + Self->Tail = Buffer; +} + + +VOID +RingBufferGetAvailableSpace( + _In_ PRING_BUFFER Self, + _Out_ size_t *AvailableSpace + ) +{ + BYTE* headSnapshot = NULL; + BYTE* tailSnapshot = NULL; + BYTE* tailPlusOne = NULL; + + ASSERT(AvailableSpace); + + // + // 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 = Self->Head; + tailSnapshot = Self->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) == Self->End) ? Self->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 = Self->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 = Self->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; + } + } +} + + +VOID +RingBufferGetAvailableData( + _In_ PRING_BUFFER Self, + _Out_ size_t *AvailableData + ) +{ + size_t availableSpace; + + ASSERT(AvailableData); + + RingBufferGetAvailableSpace(Self, &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 = Self->Size - availableSpace - 1; +} + + +NTSTATUS +RingBufferWrite( + _In_ PRING_BUFFER Self, + _In_reads_bytes_(DataSize) + BYTE* Data, + _In_ size_t DataSize + ) +{ + size_t availableSpace; + size_t bytesToCopy; + size_t spaceFromCurrToEnd; + + ASSERT(Data && (0 != DataSize)); + + if (Self->Tail >= Self->End) + { + return STATUS_INTERNAL_ERROR; + } + + // + // Get the amount of space available in the buffer + // + RingBufferGetAvailableSpace(Self, &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 ((Self->Tail + bytesToCopy) > Self->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 = Self->End - Self->Tail; + + RtlCopyMemory(Self->Tail, Data, spaceFromCurrToEnd); + + Data += spaceFromCurrToEnd; + + bytesToCopy -= spaceFromCurrToEnd; + + // + // The second step of the copy ... + // + RtlCopyMemory(Self->Base, Data, bytesToCopy); + + // + // Advance the tail pointer + // + Self->Tail = Self->Base + bytesToCopy; + } + else + { + // + // Data does NOT wrap around the end of the buffer. Just copy it + // over in a single step + // + RtlCopyMemory(Self->Tail, Data, bytesToCopy); + + // + // Advance the tail pointer + // + Self->Tail += bytesToCopy; + if (Self->Tail == Self->End) + { + // + // We have exactly reached the end of the buffer. The next + // write should wrap around and start from the beginning. + // + Self->Tail = Self->Base; + } + } + + ASSERT(Self->Tail < Self->End); + } + + return STATUS_SUCCESS; +} + + +NTSTATUS +RingBufferRead( + _In_ PRING_BUFFER Self, + _Out_writes_bytes_to_(DataSize, *BytesCopied) + BYTE* Data, + _In_ size_t DataSize, + _Out_ size_t *BytesCopied + ) +{ + size_t availableData; + size_t dataFromCurrToEnd; + + ASSERT(Data && (DataSize != 0)); + + if (Self->Head >= Self->End) + { + return STATUS_INTERNAL_ERROR; + } + + // + // Get the amount of data available in the buffer + // + RingBufferGetAvailableData(Self, &availableData); + + if (availableData == 0) + { + *BytesCopied = 0; + return STATUS_SUCCESS; + } + + if (DataSize > availableData) + { + DataSize = availableData; + } + + *BytesCopied = DataSize; + + if ((Self->Head + DataSize) > Self->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 = Self->End - Self->Head; + RtlCopyMemory(Data, Self->Head, dataFromCurrToEnd); + Data += dataFromCurrToEnd; + DataSize -= dataFromCurrToEnd; + + // + // The second step of the copy ... + // + RtlCopyMemory(Data, Self->Base, DataSize); + + // + // Advance the head pointer + // + Self->Head = Self->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. + // + RtlCopyMemory(Data, Self->Head, DataSize); + + // + // Advance the head pointer + // + Self->Head += DataSize; + if (Self->Head == Self->End) + { + // + // We have exactly reached the end of the buffer. The next + // read should wrap around and start from the beginning. + // + Self->Head = Self->Base; + } + } + + ASSERT(Self->Head < Self->End); + + return STATUS_SUCCESS; +} + diff --git a/VirtualSerial2/ringbuffer.h b/VirtualSerial2/ringbuffer.h new file mode 100644 index 0000000..8c5e3e3 --- /dev/null +++ b/VirtualSerial2/ringbuffer.h @@ -0,0 +1,117 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved + +Module Name: + + Ringbuffer.h + +--*/ + +#pragma once + +typedef struct _RING_BUFFER +{ + // + // The size in bytes of the ring buffer. + // + size_t Size; + + // + // A pointer to the base of the ring buffer. + // + BYTE* Base; + + // + // A pointer to the byte beyond the end of the ring buffer. Used for + // quick comparisons when determining if we need to wrap. + // + BYTE* 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. + // + // + BYTE* 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. + // + BYTE* Tail; + +} RING_BUFFER, *PRING_BUFFER; + + +VOID +RingBufferInitialize( + _In_ PRING_BUFFER Self, + _In_reads_bytes_(BufferSize) + BYTE* Buffer, + _In_ size_t BufferSize + ); + +NTSTATUS +RingBufferWrite( + _In_ PRING_BUFFER Self, + _In_reads_bytes_(DataSize) + BYTE* Data, + _In_ size_t DataSize + ); + +NTSTATUS +RingBufferRead( + _In_ PRING_BUFFER Self, + _Out_writes_bytes_to_(DataSize, *BytesCopied) + BYTE* Data, + _In_ size_t DataSize, + _Out_ size_t *BytesCopied + ); + +VOID +RingBufferGetAvailableSpace( + _In_ PRING_BUFFER Self, + _Out_ size_t *AvailableSpace + ); + +VOID +RingBufferGetAvailableData( + _In_ PRING_BUFFER Self, + _Out_ size_t *AvailableData + ); diff --git a/VirtualSerial2/serial.h b/VirtualSerial2/serial.h new file mode 100644 index 0000000..223c1ce --- /dev/null +++ b/VirtualSerial2/serial.h @@ -0,0 +1,128 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved + +Module Name: + + Serial.h + +Abstract: + + Type definitions and data for the serial port driver + +--*/ + +#pragma once + +// +// 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) + +#ifdef _KERNEL_MODE + +#include + +#else + +//////////////////////////////////////////////////////////////////////////////// +// +// Instead of #include , the following are copied from that header, +// as ntddser.h is conflicted with winioctl.h which is included from wdf.h +// +//////////////////////////////////////////////////////////////////////////////// + +#define IOCTL_SERIAL_SET_BAUD_RATE CTL_CODE(FILE_DEVICE_SERIAL_PORT, 1,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_QUEUE_SIZE CTL_CODE(FILE_DEVICE_SERIAL_PORT, 2,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_LINE_CONTROL CTL_CODE(FILE_DEVICE_SERIAL_PORT, 3,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_BREAK_ON CTL_CODE(FILE_DEVICE_SERIAL_PORT, 4,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_BREAK_OFF CTL_CODE(FILE_DEVICE_SERIAL_PORT, 5,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_IMMEDIATE_CHAR CTL_CODE(FILE_DEVICE_SERIAL_PORT, 6,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_TIMEOUTS CTL_CODE(FILE_DEVICE_SERIAL_PORT, 7,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_GET_TIMEOUTS CTL_CODE(FILE_DEVICE_SERIAL_PORT, 8,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_DTR CTL_CODE(FILE_DEVICE_SERIAL_PORT, 9,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_CLR_DTR CTL_CODE(FILE_DEVICE_SERIAL_PORT,10,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_RESET_DEVICE CTL_CODE(FILE_DEVICE_SERIAL_PORT,11,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_RTS CTL_CODE(FILE_DEVICE_SERIAL_PORT,12,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_CLR_RTS CTL_CODE(FILE_DEVICE_SERIAL_PORT,13,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_XOFF CTL_CODE(FILE_DEVICE_SERIAL_PORT,14,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_XON CTL_CODE(FILE_DEVICE_SERIAL_PORT,15,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_GET_WAIT_MASK CTL_CODE(FILE_DEVICE_SERIAL_PORT,16,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_WAIT_MASK CTL_CODE(FILE_DEVICE_SERIAL_PORT,17,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_WAIT_ON_MASK CTL_CODE(FILE_DEVICE_SERIAL_PORT,18,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_PURGE CTL_CODE(FILE_DEVICE_SERIAL_PORT,19,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_GET_BAUD_RATE CTL_CODE(FILE_DEVICE_SERIAL_PORT,20,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_GET_LINE_CONTROL CTL_CODE(FILE_DEVICE_SERIAL_PORT,21,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_GET_CHARS CTL_CODE(FILE_DEVICE_SERIAL_PORT,22,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_CHARS CTL_CODE(FILE_DEVICE_SERIAL_PORT,23,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_GET_HANDFLOW CTL_CODE(FILE_DEVICE_SERIAL_PORT,24,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_HANDFLOW CTL_CODE(FILE_DEVICE_SERIAL_PORT,25,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_GET_MODEMSTATUS CTL_CODE(FILE_DEVICE_SERIAL_PORT,26,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_GET_COMMSTATUS CTL_CODE(FILE_DEVICE_SERIAL_PORT,27,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_XOFF_COUNTER CTL_CODE(FILE_DEVICE_SERIAL_PORT,28,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_GET_PROPERTIES CTL_CODE(FILE_DEVICE_SERIAL_PORT,29,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_GET_DTRRTS CTL_CODE(FILE_DEVICE_SERIAL_PORT,30,METHOD_BUFFERED,FILE_ANY_ACCESS) + +#define IOCTL_SERIAL_GET_MODEM_CONTROL CTL_CODE(FILE_DEVICE_SERIAL_PORT,37,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_MODEM_CONTROL CTL_CODE(FILE_DEVICE_SERIAL_PORT,38,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_FIFO_CONTROL CTL_CODE(FILE_DEVICE_SERIAL_PORT,39,METHOD_BUFFERED,FILE_ANY_ACCESS) + + +typedef struct _SERIAL_BAUD_RATE { + ULONG BaudRate; + } SERIAL_BAUD_RATE,*PSERIAL_BAUD_RATE; + +typedef struct _SERIAL_LINE_CONTROL { + UCHAR StopBits; + UCHAR Parity; + UCHAR WordLength; + } SERIAL_LINE_CONTROL,*PSERIAL_LINE_CONTROL; + +typedef struct _SERIAL_TIMEOUTS { + ULONG ReadIntervalTimeout; + ULONG ReadTotalTimeoutMultiplier; + ULONG ReadTotalTimeoutConstant; + ULONG WriteTotalTimeoutMultiplier; + ULONG WriteTotalTimeoutConstant; + } SERIAL_TIMEOUTS,*PSERIAL_TIMEOUTS; + +#define STOP_BIT_1 0 +#define STOP_BITS_1_5 1 +#define STOP_BITS_2 2 + +#define NO_PARITY 0 +#define ODD_PARITY 1 +#define EVEN_PARITY 2 +#define MARK_PARITY 3 +#define SPACE_PARITY 4 + +#endif // #ifdef _KERNEL_MODE, #include + +// +// DEFINE_GUID(GUID_DEVINTERFACE_MODEM, 0x2c7089aa, 0x2e0e, 0x11d1, 0xb1, 0x14, 0x00, 0xc0, 0x4f, 0xc2, 0xaa, 0xe4); +// +#include