mirror of
https://github.com/ekinnee/SharpCAT.git
synced 2025-12-06 03:31:59 +01:00
Compare commits
22 commits
89878cda10
...
2c83ce3791
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2c83ce3791 | ||
|
|
1e54b4ca10 | ||
|
|
65648538c6 | ||
|
|
416fcd98c9 | ||
|
|
99ac9e3b59 | ||
|
|
1c02dc4cac | ||
|
|
4192efdd0c | ||
|
|
cae56ac617 | ||
|
|
5d4f39cb67 | ||
|
|
88b7e3f6b9 | ||
|
|
950d840238 | ||
|
|
ff341a3fed | ||
|
|
07e2d349bf | ||
|
|
339b72374f | ||
|
|
c20d3a12bb | ||
|
|
ce86879a3b | ||
|
|
b9287c3ecf | ||
|
|
82983da270 | ||
|
|
a16d2e4b92 | ||
|
|
c80f3e89a8 | ||
|
|
bcbafbf8b3 | ||
|
|
1881bfb8cc |
10
.vscode/tasks.json
vendored
10
.vscode/tasks.json
vendored
|
|
@ -7,7 +7,7 @@
|
|||
"type": "process",
|
||||
"args": [
|
||||
"build",
|
||||
"${workspaceFolder}/SharpCAT/SharpCATLib.csproj",
|
||||
"${workspaceFolder}/Library/Library.csproj",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary"
|
||||
],
|
||||
|
|
@ -23,7 +23,7 @@
|
|||
"type": "process",
|
||||
"args": [
|
||||
"publish",
|
||||
"${workspaceFolder}/SharpCAT/SharpCATLib.csproj",
|
||||
"${workspaceFolder}/Library/Library.csproj",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary"
|
||||
],
|
||||
|
|
@ -37,7 +37,7 @@
|
|||
"watch",
|
||||
"run",
|
||||
"--project",
|
||||
"${workspaceFolder}/SharpCAT/SharpCATLib.csproj"
|
||||
"${workspaceFolder}/Library/Library.csproj"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
|
|
@ -47,7 +47,7 @@
|
|||
"type": "process",
|
||||
"args": [
|
||||
"clean",
|
||||
"${workspaceFolder}/SharpCAT/SharpCATLib.csproj",
|
||||
"${workspaceFolder}/Library/Library.csproj",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary"
|
||||
],
|
||||
|
|
@ -59,7 +59,7 @@
|
|||
"type": "process",
|
||||
"args": [
|
||||
"restore",
|
||||
"${workspaceFolder}/SharpCAT/SharpCATLib.csproj"
|
||||
"${workspaceFolder}/Library/Library.csproj"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
}
|
||||
|
|
|
|||
216
Client/Models/ApiModels.cs
Normal file
216
Client/Models/ApiModels.cs
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace SharpCAT.Client.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Parity settings for serial communication
|
||||
/// </summary>
|
||||
public enum Parity
|
||||
{
|
||||
/// <summary>
|
||||
/// No parity checking
|
||||
/// </summary>
|
||||
None = 0,
|
||||
/// <summary>
|
||||
/// Odd parity checking
|
||||
/// </summary>
|
||||
Odd = 1,
|
||||
/// <summary>
|
||||
/// Even parity checking
|
||||
/// </summary>
|
||||
Even = 2,
|
||||
/// <summary>
|
||||
/// Mark parity checking
|
||||
/// </summary>
|
||||
Mark = 3,
|
||||
/// <summary>
|
||||
/// Space parity checking
|
||||
/// </summary>
|
||||
Space = 4
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stop bits settings for serial communication
|
||||
/// </summary>
|
||||
public enum StopBits
|
||||
{
|
||||
/// <summary>
|
||||
/// No stop bits
|
||||
/// </summary>
|
||||
None = 0,
|
||||
/// <summary>
|
||||
/// One stop bit
|
||||
/// </summary>
|
||||
One = 1,
|
||||
/// <summary>
|
||||
/// Two stop bits
|
||||
/// </summary>
|
||||
Two = 2,
|
||||
/// <summary>
|
||||
/// One and a half stop bits
|
||||
/// </summary>
|
||||
OnePointFive = 3
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handshake settings for serial communication
|
||||
/// </summary>
|
||||
public enum Handshake
|
||||
{
|
||||
/// <summary>
|
||||
/// No flow control
|
||||
/// </summary>
|
||||
None = 0,
|
||||
/// <summary>
|
||||
/// XOn/XOff software flow control
|
||||
/// </summary>
|
||||
XOnXOff = 1,
|
||||
/// <summary>
|
||||
/// RTS hardware flow control
|
||||
/// </summary>
|
||||
RequestToSend = 2,
|
||||
/// <summary>
|
||||
/// Both RTS and XOn/XOff flow control
|
||||
/// </summary>
|
||||
RequestToSendXOnXOff = 3
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Response model for listing available serial ports
|
||||
/// </summary>
|
||||
public class PortListResponse
|
||||
{
|
||||
/// <summary>
|
||||
/// Array of available serial port names
|
||||
/// </summary>
|
||||
public string[] Ports { get; set; } = new string[0];
|
||||
|
||||
/// <summary>
|
||||
/// Number of available ports
|
||||
/// </summary>
|
||||
public int Count => Ports.Length;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Request model for opening a serial port
|
||||
/// </summary>
|
||||
public class OpenPortRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// Name of the serial port to open (e.g., "COM1", "/dev/ttyUSB0")
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string PortName { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Baud rate for communication (default: 9600)
|
||||
/// </summary>
|
||||
public int BaudRate { get; set; } = 9600;
|
||||
|
||||
/// <summary>
|
||||
/// Parity setting (default: None)
|
||||
/// </summary>
|
||||
public Parity Parity { get; set; } = Parity.None;
|
||||
|
||||
/// <summary>
|
||||
/// Stop bits setting (default: One)
|
||||
/// </summary>
|
||||
public StopBits StopBits { get; set; } = StopBits.One;
|
||||
|
||||
/// <summary>
|
||||
/// Handshake setting (default: None)
|
||||
/// </summary>
|
||||
public Handshake Handshake { get; set; } = Handshake.None;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Response model for port operations
|
||||
/// </summary>
|
||||
public class PortOperationResponse
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates if the operation was successful
|
||||
/// </summary>
|
||||
public bool Success { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Human-readable message about the operation
|
||||
/// </summary>
|
||||
public string Message { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Name of the port that was operated on
|
||||
/// </summary>
|
||||
public string? PortName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Current status of the port (open/closed)
|
||||
/// </summary>
|
||||
public bool IsOpen { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Request model for sending CAT commands
|
||||
/// </summary>
|
||||
public class SendCommandRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// CAT command string to send to the radio
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string Command { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Response model for CAT command operations
|
||||
/// </summary>
|
||||
public class CommandResponse
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates if the command was sent successfully
|
||||
/// </summary>
|
||||
public bool Success { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The command that was sent
|
||||
/// </summary>
|
||||
public string Command { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Response received from the radio (if any)
|
||||
/// </summary>
|
||||
public string? Response { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Human-readable message about the operation
|
||||
/// </summary>
|
||||
public string Message { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Timestamp when the command was executed
|
||||
/// </summary>
|
||||
public DateTime Timestamp { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Response model for errors
|
||||
/// </summary>
|
||||
public class ErrorResponse
|
||||
{
|
||||
/// <summary>
|
||||
/// Error message
|
||||
/// </summary>
|
||||
public string Error { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Additional details about the error
|
||||
/// </summary>
|
||||
public string? Details { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Timestamp when the error occurred
|
||||
/// </summary>
|
||||
public DateTime Timestamp { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
196
Client/README.md
Normal file
196
Client/README.md
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
# SharpCAT.Client
|
||||
|
||||
A .NET Standard 2.0 client library that provides a typed C# API for consuming the SharpCAT Server HTTP API endpoints. This library makes it easy to integrate CAT (Computer Aided Transceiver) control into your .NET applications.
|
||||
|
||||
## Features
|
||||
|
||||
- **Typed API**: Strongly-typed C# methods for all SharpCAT Server endpoints
|
||||
- **Async/Await Support**: Modern async programming with cancellation token support
|
||||
- **Error Handling**: Comprehensive error handling with custom exceptions
|
||||
- **Cross-Platform**: .NET Standard 2.0 compatible (works with .NET Core, .NET Framework, .NET 5+)
|
||||
- **HTTP Client Integration**: Uses System.Net.Http.Json for JSON serialization
|
||||
- **Frequency Helpers**: Convenient methods for getting/setting radio frequencies
|
||||
- **XML Documentation**: Full IntelliSense support with comprehensive documentation
|
||||
|
||||
## Installation
|
||||
|
||||
Add the SharpCAT.Client project reference to your application:
|
||||
|
||||
```xml
|
||||
<ProjectReference Include="path/to/SharpCAT.Client/SharpCAT.Client.csproj" />
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Basic Usage
|
||||
|
||||
```csharp
|
||||
using SharpCAT.Client;
|
||||
using SharpCAT.Client.Models;
|
||||
|
||||
// Create client instance
|
||||
using var client = new SharpCATClient("http://localhost:5188");
|
||||
|
||||
// Get available serial ports
|
||||
var ports = await client.GetPortsAsync();
|
||||
Console.WriteLine($"Available ports: {string.Join(", ", ports.Ports)}");
|
||||
|
||||
// Open a serial port
|
||||
var openRequest = new OpenPortRequest
|
||||
{
|
||||
PortName = "COM3",
|
||||
BaudRate = 9600
|
||||
};
|
||||
var openResult = await client.OpenPortAsync(openRequest);
|
||||
Console.WriteLine($"Port opened: {openResult.Success}");
|
||||
|
||||
// Send a raw CAT command
|
||||
var commandResult = await client.SendCommandAsync("FA;");
|
||||
Console.WriteLine($"Command response: {commandResult.Response}");
|
||||
|
||||
// Use convenience methods for frequency operations
|
||||
var frequency = await client.GetFrequencyAsync();
|
||||
Console.WriteLine($"Current frequency: {frequency} Hz");
|
||||
|
||||
await client.SetFrequencyAsync(14074000); // Set to 14.074 MHz
|
||||
```
|
||||
|
||||
### Advanced Usage
|
||||
|
||||
```csharp
|
||||
// Use with dependency injection and HttpClientFactory
|
||||
services.AddHttpClient<SharpCATClient>(client =>
|
||||
{
|
||||
client.BaseAddress = new Uri("http://localhost:5188");
|
||||
});
|
||||
|
||||
// Handle errors
|
||||
try
|
||||
{
|
||||
var result = await client.SendCommandAsync("invalid_command");
|
||||
}
|
||||
catch (SharpCATClientException ex)
|
||||
{
|
||||
Console.WriteLine($"Error: {ex.Message}");
|
||||
if (ex.Details != null)
|
||||
Console.WriteLine($"Details: {ex.Details}");
|
||||
}
|
||||
|
||||
// Use cancellation tokens
|
||||
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));
|
||||
var status = await client.GetStatusAsync(cts.Token);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### Core Methods
|
||||
|
||||
#### Port Management
|
||||
- `GetPortsAsync()` - Get all available serial ports
|
||||
- `OpenPortAsync(request)` - Open and configure a serial port
|
||||
- `ClosePortAsync()` - Close the current serial port
|
||||
- `GetStatusAsync()` - Get current port connection status
|
||||
|
||||
#### CAT Commands
|
||||
- `SendCommandAsync(command)` - Send a raw CAT command string
|
||||
- `SendCommandAsync(request)` - Send a CAT command with full request object
|
||||
|
||||
#### Frequency Operations (Convenience Methods)
|
||||
- `GetFrequencyAsync()` - Get current frequency (VFO A)
|
||||
- `SetFrequencyAsync(frequencyHz)` - Set frequency (VFO A)
|
||||
- `GetFrequencyBAsync()` - Get current frequency (VFO B)
|
||||
- `SetFrequencyBAsync(frequencyHz)` - Set frequency (VFO B)
|
||||
|
||||
### Configuration Options
|
||||
|
||||
When opening a port, you can configure:
|
||||
|
||||
```csharp
|
||||
var request = new OpenPortRequest
|
||||
{
|
||||
PortName = "COM3", // Required
|
||||
BaudRate = 9600, // Default: 9600
|
||||
Parity = Parity.None, // Default: None
|
||||
StopBits = StopBits.One, // Default: One
|
||||
Handshake = Handshake.None // Default: None
|
||||
};
|
||||
```
|
||||
|
||||
### Error Handling
|
||||
|
||||
The client throws `SharpCATClientException` for various error conditions:
|
||||
|
||||
- HTTP request failures
|
||||
- Network timeouts
|
||||
- API error responses
|
||||
- Deserialization failures
|
||||
|
||||
```csharp
|
||||
try
|
||||
{
|
||||
var result = await client.GetPortsAsync();
|
||||
}
|
||||
catch (SharpCATClientException ex)
|
||||
{
|
||||
// Handle SharpCAT-specific errors
|
||||
Console.WriteLine($"SharpCAT Error: {ex.Message}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Handle other errors
|
||||
Console.WriteLine($"Unexpected error: {ex.Message}");
|
||||
}
|
||||
```
|
||||
|
||||
## Common CAT Commands
|
||||
|
||||
Here are some common CAT commands you can send using `SendCommandAsync()`:
|
||||
|
||||
| Command | Description | Example Response |
|
||||
|---------|-------------|------------------|
|
||||
| `FA;` | Get VFO A frequency | `FA00014074000;` |
|
||||
| `FB;` | Get VFO B frequency | `FB00007074000;` |
|
||||
| `FA14074000;` | Set VFO A frequency | No response or `FA14074000;` |
|
||||
| `MD;` | Get operating mode | `MD2;` (USB) |
|
||||
| `MD2;` | Set mode to USB | No response |
|
||||
| `IF;` | Get radio information | `IF00014074000...;` |
|
||||
|
||||
**Note**: The exact commands and responses depend on your radio model. Consult your radio's CAT documentation for complete command reference.
|
||||
|
||||
## Thread Safety
|
||||
|
||||
The `SharpCATClient` class is thread-safe for concurrent read operations, but write operations (like sending commands) should be serialized to avoid conflicts at the radio level.
|
||||
|
||||
## Disposal
|
||||
|
||||
The client implements `IDisposable`. When you create a client with a base address string or URI, it owns the internal `HttpClient` and will dispose it. If you pass in your own `HttpClient`, the client will not dispose it.
|
||||
|
||||
```csharp
|
||||
// Client owns HttpClient - will be disposed
|
||||
using var client = new SharpCATClient("http://localhost:5188");
|
||||
|
||||
// You own HttpClient - manage disposal yourself
|
||||
var httpClient = new HttpClient();
|
||||
var client = new SharpCATClient(httpClient);
|
||||
// Don't forget to dispose httpClient when done
|
||||
```
|
||||
|
||||
## Requirements
|
||||
|
||||
- .NET Standard 2.0 or later
|
||||
- SharpCAT Server running and accessible
|
||||
- Network connectivity to the server
|
||||
|
||||
## Dependencies
|
||||
|
||||
- System.Net.Http.Json (5.0.0)
|
||||
- System.Text.Json (5.0.2)
|
||||
- System.ComponentModel.Annotations (5.0.0)
|
||||
|
||||
## License
|
||||
|
||||
This project follows the same license as the parent SharpCAT project.
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are welcome! Please follow the existing code style and add appropriate documentation for new features.
|
||||
17
Client/SharpCAT.Client.csproj
Normal file
17
Client/SharpCAT.Client.csproj
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<NoWarn>$(NoWarn);1591</NoWarn>
|
||||
<LangVersion>9.0</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Net.Http.Json" Version="5.0.0" />
|
||||
<PackageReference Include="System.Text.Json" Version="5.0.2" />
|
||||
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
325
Client/SharpCATClient.cs
Normal file
325
Client/SharpCATClient.cs
Normal file
|
|
@ -0,0 +1,325 @@
|
|||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCAT.Client.Models;
|
||||
|
||||
namespace SharpCAT.Client
|
||||
{
|
||||
/// <summary>
|
||||
/// Client for accessing SharpCAT Server API endpoints
|
||||
/// </summary>
|
||||
public class SharpCATClient : IDisposable
|
||||
{
|
||||
private readonly HttpClient _httpClient;
|
||||
private readonly bool _ownsHttpClient;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the base address of the SharpCAT server
|
||||
/// </summary>
|
||||
public Uri BaseAddress => _httpClient.BaseAddress ?? throw new InvalidOperationException("Base address not set");
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the SharpCATClient with the specified base address
|
||||
/// </summary>
|
||||
/// <param name="baseAddress">Base URL of the SharpCAT server (e.g., "http://localhost:5188")</param>
|
||||
public SharpCATClient(string baseAddress) : this(new Uri(baseAddress))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the SharpCATClient with the specified base address
|
||||
/// </summary>
|
||||
/// <param name="baseAddress">Base URI of the SharpCAT server</param>
|
||||
public SharpCATClient(Uri baseAddress)
|
||||
{
|
||||
_httpClient = new HttpClient { BaseAddress = baseAddress };
|
||||
_ownsHttpClient = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the SharpCATClient with an existing HttpClient
|
||||
/// </summary>
|
||||
/// <param name="httpClient">Configured HttpClient instance</param>
|
||||
public SharpCATClient(HttpClient httpClient)
|
||||
{
|
||||
_httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
|
||||
_ownsHttpClient = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all available serial ports on the system
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">Cancellation token</param>
|
||||
/// <returns>List of available serial ports</returns>
|
||||
/// <exception cref="SharpCATClientException">Thrown when the API call fails</exception>
|
||||
public async Task<PortListResponse> GetPortsAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
var response = await _httpClient.GetFromJsonAsync<PortListResponse>("api/cat/ports", cancellationToken);
|
||||
return response ?? throw new SharpCATClientException("Failed to deserialize response");
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
throw new SharpCATClientException("Failed to get ports", ex);
|
||||
}
|
||||
catch (TaskCanceledException ex)
|
||||
{
|
||||
throw new SharpCATClientException("Request timed out", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens and configures a serial port for communication
|
||||
/// </summary>
|
||||
/// <param name="request">Port configuration parameters</param>
|
||||
/// <param name="cancellationToken">Cancellation token</param>
|
||||
/// <returns>Result of the port open operation</returns>
|
||||
/// <exception cref="ArgumentNullException">Thrown when request is null</exception>
|
||||
/// <exception cref="SharpCATClientException">Thrown when the API call fails</exception>
|
||||
public async Task<PortOperationResponse> OpenPortAsync(OpenPortRequest request, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (request == null) throw new ArgumentNullException(nameof(request));
|
||||
|
||||
try
|
||||
{
|
||||
var httpResponse = await _httpClient.PostAsJsonAsync("api/cat/open", request, cancellationToken);
|
||||
|
||||
if (httpResponse.IsSuccessStatusCode)
|
||||
{
|
||||
var response = await httpResponse.Content.ReadFromJsonAsync<PortOperationResponse>(cancellationToken: cancellationToken);
|
||||
return response ?? throw new SharpCATClientException("Failed to deserialize response");
|
||||
}
|
||||
else
|
||||
{
|
||||
var errorResponse = await httpResponse.Content.ReadFromJsonAsync<ErrorResponse>(cancellationToken: cancellationToken);
|
||||
throw new SharpCATClientException($"API returned error: {errorResponse?.Error ?? "Unknown error"}", errorResponse?.Details);
|
||||
}
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
throw new SharpCATClientException("Failed to open port", ex);
|
||||
}
|
||||
catch (TaskCanceledException ex)
|
||||
{
|
||||
throw new SharpCATClientException("Request timed out", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes the currently opened serial port
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">Cancellation token</param>
|
||||
/// <returns>Result of the port close operation</returns>
|
||||
/// <exception cref="SharpCATClientException">Thrown when the API call fails</exception>
|
||||
public async Task<PortOperationResponse> ClosePortAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
var httpResponse = await _httpClient.PostAsync("api/cat/close", null, cancellationToken);
|
||||
|
||||
if (httpResponse.IsSuccessStatusCode)
|
||||
{
|
||||
var response = await httpResponse.Content.ReadFromJsonAsync<PortOperationResponse>(cancellationToken: cancellationToken);
|
||||
return response ?? throw new SharpCATClientException("Failed to deserialize response");
|
||||
}
|
||||
else
|
||||
{
|
||||
var errorResponse = await httpResponse.Content.ReadFromJsonAsync<ErrorResponse>(cancellationToken: cancellationToken);
|
||||
throw new SharpCATClientException($"API returned error: {errorResponse?.Error ?? "Unknown error"}", errorResponse?.Details);
|
||||
}
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
throw new SharpCATClientException("Failed to close port", ex);
|
||||
}
|
||||
catch (TaskCanceledException ex)
|
||||
{
|
||||
throw new SharpCATClientException("Request timed out", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current status of the serial port connection
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">Cancellation token</param>
|
||||
/// <returns>Current port status</returns>
|
||||
/// <exception cref="SharpCATClientException">Thrown when the API call fails</exception>
|
||||
public async Task<PortOperationResponse> GetStatusAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
var response = await _httpClient.GetFromJsonAsync<PortOperationResponse>("api/cat/status", cancellationToken);
|
||||
return response ?? throw new SharpCATClientException("Failed to deserialize response");
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
throw new SharpCATClientException("Failed to get status", ex);
|
||||
}
|
||||
catch (TaskCanceledException ex)
|
||||
{
|
||||
throw new SharpCATClientException("Request timed out", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a CAT command to the connected radio
|
||||
/// </summary>
|
||||
/// <param name="request">CAT command to send</param>
|
||||
/// <param name="cancellationToken">Cancellation token</param>
|
||||
/// <returns>Result of the command operation including any response from the radio</returns>
|
||||
/// <exception cref="ArgumentNullException">Thrown when request is null</exception>
|
||||
/// <exception cref="SharpCATClientException">Thrown when the API call fails</exception>
|
||||
public async Task<CommandResponse> SendCommandAsync(SendCommandRequest request, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (request == null) throw new ArgumentNullException(nameof(request));
|
||||
|
||||
try
|
||||
{
|
||||
var httpResponse = await _httpClient.PostAsJsonAsync("api/cat/command", request, cancellationToken);
|
||||
|
||||
if (httpResponse.IsSuccessStatusCode)
|
||||
{
|
||||
var response = await httpResponse.Content.ReadFromJsonAsync<CommandResponse>(cancellationToken: cancellationToken);
|
||||
return response ?? throw new SharpCATClientException("Failed to deserialize response");
|
||||
}
|
||||
else
|
||||
{
|
||||
var errorResponse = await httpResponse.Content.ReadFromJsonAsync<ErrorResponse>(cancellationToken: cancellationToken);
|
||||
throw new SharpCATClientException($"API returned error: {errorResponse?.Error ?? "Unknown error"}", errorResponse?.Details);
|
||||
}
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
throw new SharpCATClientException("Failed to send command", ex);
|
||||
}
|
||||
catch (TaskCanceledException ex)
|
||||
{
|
||||
throw new SharpCATClientException("Request timed out", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a CAT command to the connected radio
|
||||
/// </summary>
|
||||
/// <param name="command">CAT command string to send</param>
|
||||
/// <param name="cancellationToken">Cancellation token</param>
|
||||
/// <returns>Result of the command operation including any response from the radio</returns>
|
||||
/// <exception cref="ArgumentNullException">Thrown when command is null</exception>
|
||||
/// <exception cref="SharpCATClientException">Thrown when the API call fails</exception>
|
||||
public async Task<CommandResponse> SendCommandAsync(string command, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (command == null) throw new ArgumentNullException(nameof(command));
|
||||
|
||||
var request = new SendCommandRequest { Command = command };
|
||||
return await SendCommandAsync(request, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current frequency from the radio (VFO A)
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">Cancellation token</param>
|
||||
/// <returns>Current frequency in Hz, or null if the command failed or returned no data</returns>
|
||||
/// <exception cref="SharpCATClientException">Thrown when the API call fails</exception>
|
||||
public async Task<long?> GetFrequencyAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
var response = await SendCommandAsync("FA;", cancellationToken);
|
||||
|
||||
if (!response.Success || string.IsNullOrEmpty(response.Response))
|
||||
return null;
|
||||
|
||||
// Parse frequency response: FA00014074000; -> 14074000 Hz
|
||||
var frequencyStr = response.Response;
|
||||
if (!string.IsNullOrEmpty(frequencyStr) && frequencyStr.StartsWith("FA") && frequencyStr.EndsWith(";"))
|
||||
{
|
||||
var freqPart = frequencyStr.Substring(2, frequencyStr.Length - 3);
|
||||
if (long.TryParse(freqPart, out var frequency))
|
||||
{
|
||||
return frequency;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the frequency for the radio (VFO A)
|
||||
/// </summary>
|
||||
/// <param name="frequencyHz">Frequency in Hz</param>
|
||||
/// <param name="cancellationToken">Cancellation token</param>
|
||||
/// <returns>True if the command was sent successfully</returns>
|
||||
/// <exception cref="ArgumentOutOfRangeException">Thrown when frequency is out of valid range</exception>
|
||||
/// <exception cref="SharpCATClientException">Thrown when the API call fails</exception>
|
||||
public async Task<bool> SetFrequencyAsync(long frequencyHz, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (frequencyHz < 0 || frequencyHz > 999999999999)
|
||||
throw new ArgumentOutOfRangeException(nameof(frequencyHz), "Frequency must be between 0 and 999,999,999,999 Hz");
|
||||
|
||||
// Format frequency as 11-digit string with leading zeros
|
||||
var command = $"FA{frequencyHz:D11};";
|
||||
var response = await SendCommandAsync(command, cancellationToken);
|
||||
|
||||
return response.Success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current frequency from the radio (VFO B)
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">Cancellation token</param>
|
||||
/// <returns>Current frequency in Hz, or null if the command failed or returned no data</returns>
|
||||
/// <exception cref="SharpCATClientException">Thrown when the API call fails</exception>
|
||||
public async Task<long?> GetFrequencyBAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
var response = await SendCommandAsync("FB;", cancellationToken);
|
||||
|
||||
if (!response.Success || string.IsNullOrEmpty(response.Response))
|
||||
return null;
|
||||
|
||||
// Parse frequency response: FB00014074000; -> 14074000 Hz
|
||||
var frequencyStr = response.Response;
|
||||
if (!string.IsNullOrEmpty(frequencyStr) && frequencyStr.StartsWith("FB") && frequencyStr.EndsWith(";"))
|
||||
{
|
||||
var freqPart = frequencyStr.Substring(2, frequencyStr.Length - 3);
|
||||
if (long.TryParse(freqPart, out var frequency))
|
||||
{
|
||||
return frequency;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the frequency for the radio (VFO B)
|
||||
/// </summary>
|
||||
/// <param name="frequencyHz">Frequency in Hz</param>
|
||||
/// <param name="cancellationToken">Cancellation token</param>
|
||||
/// <returns>True if the command was sent successfully</returns>
|
||||
/// <exception cref="ArgumentOutOfRangeException">Thrown when frequency is out of valid range</exception>
|
||||
/// <exception cref="SharpCATClientException">Thrown when the API call fails</exception>
|
||||
public async Task<bool> SetFrequencyBAsync(long frequencyHz, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (frequencyHz < 0 || frequencyHz > 999999999999)
|
||||
throw new ArgumentOutOfRangeException(nameof(frequencyHz), "Frequency must be between 0 and 999,999,999,999 Hz");
|
||||
|
||||
// Format frequency as 11-digit string with leading zeros
|
||||
var command = $"FB{frequencyHz:D11};";
|
||||
var response = await SendCommandAsync(command, cancellationToken);
|
||||
|
||||
return response.Success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes the client and releases resources
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_ownsHttpClient)
|
||||
{
|
||||
_httpClient?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
60
Client/SharpCATClientException.cs
Normal file
60
Client/SharpCATClientException.cs
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
using System;
|
||||
|
||||
namespace SharpCAT.Client
|
||||
{
|
||||
/// <summary>
|
||||
/// Exception thrown by SharpCAT client operations
|
||||
/// </summary>
|
||||
public class SharpCATClientException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Additional details about the error
|
||||
/// </summary>
|
||||
public string? Details { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the SharpCATClientException class
|
||||
/// </summary>
|
||||
public SharpCATClientException()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the SharpCATClientException class with a specified error message
|
||||
/// </summary>
|
||||
/// <param name="message">The message that describes the error</param>
|
||||
public SharpCATClientException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the SharpCATClientException class with a specified error message and details
|
||||
/// </summary>
|
||||
/// <param name="message">The message that describes the error</param>
|
||||
/// <param name="details">Additional details about the error</param>
|
||||
public SharpCATClientException(string message, string? details) : base(message)
|
||||
{
|
||||
Details = details;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the SharpCATClientException class with a specified error message and a reference to the inner exception
|
||||
/// </summary>
|
||||
/// <param name="message">The message that describes the error</param>
|
||||
/// <param name="innerException">The exception that is the cause of the current exception</param>
|
||||
public SharpCATClientException(string message, Exception innerException) : base(message, innerException)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the SharpCATClientException class with a specified error message, details, and a reference to the inner exception
|
||||
/// </summary>
|
||||
/// <param name="message">The message that describes the error</param>
|
||||
/// <param name="details">Additional details about the error</param>
|
||||
/// <param name="innerException">The exception that is the cause of the current exception</param>
|
||||
public SharpCATClientException(string message, string? details, Exception innerException) : base(message, innerException)
|
||||
{
|
||||
Details = details;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace SharpCATLib
|
||||
namespace SharpCAT
|
||||
{
|
||||
class FlrigProtocol
|
||||
{
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
namespace SharpCATLib.Models
|
||||
namespace SharpCAT.Models
|
||||
{
|
||||
internal class CATCommand
|
||||
{
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
namespace SharpCATLib.Models
|
||||
namespace SharpCAT.Models
|
||||
{
|
||||
//Base Radio Model
|
||||
public partial class CATRadio : IRadio
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
namespace SharpCATLib.Models
|
||||
namespace SharpCAT.Models
|
||||
{
|
||||
internal class CIVCommand
|
||||
{
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
namespace SharpCATLib.Models
|
||||
namespace SharpCAT.Models
|
||||
{
|
||||
internal class CIVRadio : IRadio
|
||||
{
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
using System;
|
||||
namespace SharpCATLib.Models
|
||||
namespace SharpCAT.Models
|
||||
{
|
||||
partial interface IRadio
|
||||
{
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
using SharpCATLib.Models;
|
||||
using SharpCAT.Models;
|
||||
|
||||
namespace SharpCATLib.Radios.Icom
|
||||
namespace SharpCAT.Radios.Icom
|
||||
{
|
||||
internal class ID4100a : CIVRadio
|
||||
{
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
namespace SharpCATLib.Radios.Icom
|
||||
namespace SharpCAT.Radios.Icom
|
||||
{
|
||||
internal class ID880H
|
||||
{
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
namespace SharpCATLib.Radios.Kenwood
|
||||
namespace SharpCAT.Radios.Kenwood
|
||||
{
|
||||
internal class THD74A
|
||||
{
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
using SharpCATLib.Models;
|
||||
using SharpCAT.Models;
|
||||
|
||||
namespace SharpCATLib.Radios.Yaesu
|
||||
namespace SharpCAT.Radios.Yaesu
|
||||
{
|
||||
public class FT818 : CATRadio
|
||||
{
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
using System;
|
||||
using System.IO.Ports;
|
||||
|
||||
namespace SharpCATLib
|
||||
namespace SharpCAT
|
||||
{
|
||||
public class Serial
|
||||
{
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
namespace SharpCATLib
|
||||
namespace SharpCAT
|
||||
{
|
||||
internal class SerialClient
|
||||
{
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
namespace SharpCATLib
|
||||
namespace SharpCAT
|
||||
{
|
||||
internal class SerialServer
|
||||
{
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO.Ports;
|
||||
|
||||
namespace SharpCATLib
|
||||
namespace SharpCAT
|
||||
{
|
||||
public class SharpCAT
|
||||
{
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
namespace SharpCATLib
|
||||
namespace SharpCAT
|
||||
{
|
||||
internal class Socket
|
||||
{
|
||||
150
README.md
150
README.md
|
|
@ -1,93 +1,129 @@
|
|||
# This like many, are ideas. There may or may not be progress as inspiration and time allows.
|
||||
|
||||
# SharpCAT
|
||||
C#, .NET Standard based CAT control library.
|
||||
|
||||
I am targeting .Net Standard so that the assembly may be used with .Net Core or the .Net framework.
|
||||
**SharpCAT** is a C#/.NET cross-platform library and Web API server for CAT (Computer Aided Transceiver) radio control. It supports serial port CAT operations, modern async programming, and provides a REST API for remote radio control.
|
||||
|
||||
## Development Setup
|
||||
---
|
||||
|
||||
## Features
|
||||
|
||||
- **Core Library**: .NET Standard-based, works with .NET Core and .NET Framework
|
||||
- **Cross-platform Server**: ASP.NET Core Web API server, works on Windows, Linux, and macOS
|
||||
- **REST API**: Endpoints for serial port management and sending arbitrary CAT commands
|
||||
- **Swagger/OpenAPI**: Interactive API docs and testing interface
|
||||
- **Async/Await**: Modern async support for non-blocking IO
|
||||
- **Extensible**: Designed for adding radios and protocols (FT818, ID-4100A, TH-D74A, etc.)
|
||||
- **Built-in Logging and Error Handling**
|
||||
|
||||
---
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- [.NET 8.0 SDK](https://dotnet.microsoft.com/download) or later
|
||||
- [Visual Studio Code](https://code.visualstudio.com/)
|
||||
- [C# Extension for VS Code](https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csharp)
|
||||
|
||||
### Building and Running
|
||||
### Running the Web API Server
|
||||
|
||||
This project is configured for development in Visual Studio Code with the .NET CLI.
|
||||
|
||||
**To build the project:**
|
||||
```bash
|
||||
dotnet build SharpCAT/SharpCATLib.csproj
|
||||
cd Server/SharpCAT.Server
|
||||
dotnet run
|
||||
```
|
||||
The server listens on `http://localhost:5188` by default.
|
||||
Open [http://localhost:5188](http://localhost:5188) in your browser for the Swagger UI.
|
||||
|
||||
---
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### List Available Serial Ports
|
||||
|
||||
```bash
|
||||
curl http://localhost:5188/api/cat/ports
|
||||
```
|
||||
|
||||
**To clean the project:**
|
||||
### Open a Serial Port
|
||||
|
||||
```bash
|
||||
dotnet clean SharpCAT/SharpCATLib.csproj
|
||||
curl -X POST http://localhost:5188/api/cat/open \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"portName": "COM3", "baudRate": 9600}'
|
||||
```
|
||||
|
||||
**To restore packages:**
|
||||
### Send a CAT Command
|
||||
|
||||
```bash
|
||||
dotnet restore SharpCAT/SharpCATLib.csproj
|
||||
curl -X POST http://localhost:5188/api/cat/command \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"command": "FA;"}'
|
||||
```
|
||||
|
||||
**VS Code Tasks:**
|
||||
The repository includes pre-configured VS Code tasks (`Ctrl+Shift+P` → "Tasks: Run Task"):
|
||||
- **build** - Builds the project (default build task: `Ctrl+Shift+B`)
|
||||
- **clean** - Cleans build outputs
|
||||
- **restore** - Restores NuGet packages
|
||||
- **publish** - Publishes the library
|
||||
- **watch** - Watches for changes and rebuilds automatically
|
||||
---
|
||||
|
||||
I'm starting with the FT818, I then plan on adding my ID-4100a, and TH-D74A.
|
||||
## Project Structure
|
||||
|
||||
If you wish to help let me know, or create a pull request. I'm not a pro developer, just a hack
|
||||
that normally builds small tools for himself.
|
||||
```
|
||||
Library/ # Core CAT library (cross-platform)
|
||||
Server/SharpCAT.Server/ # ASP.NET Core Web API server
|
||||
```
|
||||
|
||||
Ideas on how to do this are appreciated!
|
||||
---
|
||||
|
||||
# Why?
|
||||
Yes, there's Hamlib, and yes there's HamLibSharp.
|
||||
## Development
|
||||
|
||||
https://github.com/N0NB/hamlib
|
||||
### Build Everything
|
||||
|
||||
https://github.com/k5jae/HamLibSharp
|
||||
```bash
|
||||
dotnet build
|
||||
```
|
||||
|
||||
I don't speak C++ and PInvoke is nasty.
|
||||
### Build Just the Core Library
|
||||
|
||||
That being said, there's also not a pure .Net (C#) CAT control lib out there that I know of.
|
||||
```bash
|
||||
dotnet build Library/Library.csproj
|
||||
```
|
||||
|
||||
# What needs done?
|
||||
The project is in an early very phase. The following is in no particular order, except the IRadio bit.
|
||||
### VS Code Tasks
|
||||
|
||||
1. Implement an IRadio interface.
|
||||
This repo includes VS Code tasks for building, cleaning, restoring, publishing, and watching changes.
|
||||
|
||||
2. Settle on how the radios and commands are defined.
|
||||
---
|
||||
|
||||
JSON?
|
||||
|
||||
Or just use a .cs file (current)?
|
||||
|
||||
3. Need to support opening / using an arbitrary number of radios.
|
||||
## Roadmap & Ideas
|
||||
|
||||
RigControl can do 2.
|
||||
|
||||
4. Fully support asynchronous operations.
|
||||
- Implement generic IRadio interface and radio-specific drivers
|
||||
- Support multiple concurrent radios
|
||||
- Fully async serial operations
|
||||
- Remote sharing of serial ports
|
||||
- Implement flrig protocol support
|
||||
- Potential AGWPE integration
|
||||
- Service/daemon operation support
|
||||
- Additional radio protocols (CAT, CI-V, etc.)
|
||||
|
||||
There's some handy events exposed by .net for data received on the Serial Port.
|
||||
|
||||
5. Add support for remote sharing of serial ports.
|
||||
---
|
||||
|
||||
6. Implemtment the flrig control protocol.
|
||||
|
||||
Use this in place of flrig if desired.
|
||||
## Why?
|
||||
|
||||
7. Maybe implement AGWPE?
|
||||
While Hamlib and HamLibSharp exist, there is a need for a modern, pure .NET CAT control library and server for direct integration with C# projects, without C++/PInvoke dependencies.
|
||||
|
||||
8. Implement the ability to run as a service.
|
||||
---
|
||||
|
||||
9. Implement CAT and CIV control.
|
||||
## Contributing
|
||||
|
||||
This will come with the first Icom radio implemented I figure.
|
||||
|
||||
I've switched radios, so I have a Yaesu FT991a, Baofeng BF-T1, WLN KD-C1, Baofeng UV-5x3, TH-D72a, FT-2DR and an D878UV.
|
||||
Contributions are very welcome! Please open issues, submit PRs, or share your ideas.
|
||||
|
||||
---
|
||||
|
||||
## License
|
||||
|
||||
This project follows the same license as the parent SharpCAT project.
|
||||
|
||||
---
|
||||
|
||||
## Credits
|
||||
|
||||
- Inspired by FT818, ID-4100A, TH-D74A, Yaesu FT991a, and others
|
||||
- Thanks to all open source contributors and radio amateurs for protocol documentation and ideas
|
||||
|
||||
---
|
||||
|
||||
*If you wish to help, let me know or create a pull request. I'm not a pro developer, just a hobbyist building tools for myself and others.*
|
||||
|
|
|
|||
292
Server/Controllers/CatController.cs
Normal file
292
Server/Controllers/CatController.cs
Normal file
|
|
@ -0,0 +1,292 @@
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using Server.Models;
|
||||
using Server.Services;
|
||||
|
||||
namespace Server.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// Controller for CAT (Computer Aided Transceiver) operations
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
[Produces("application/json")]
|
||||
public class CatController : ControllerBase
|
||||
{
|
||||
private readonly ISerialCommunicationService _serialService;
|
||||
private readonly ILogger<CatController> _logger;
|
||||
|
||||
public CatController(ISerialCommunicationService serialService, ILogger<CatController> logger)
|
||||
{
|
||||
_serialService = serialService ?? throw new ArgumentNullException(nameof(serialService));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all available serial ports on the system
|
||||
/// </summary>
|
||||
/// <returns>List of available serial ports</returns>
|
||||
/// <response code="200">Returns the list of available serial ports</response>
|
||||
[HttpGet("ports")]
|
||||
[ProducesResponseType(typeof(PortListResponse), StatusCodes.Status200OK)]
|
||||
public ActionResult<PortListResponse> GetPorts()
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("Getting available serial ports");
|
||||
var ports = _serialService.GetAvailablePorts();
|
||||
|
||||
var response = new PortListResponse
|
||||
{
|
||||
Ports = ports
|
||||
};
|
||||
|
||||
_logger.LogInformation("Found {PortCount} available ports", response.Count);
|
||||
return Ok(response);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error getting available ports");
|
||||
return StatusCode(500, new ErrorResponse
|
||||
{
|
||||
Error = "Internal server error while getting ports",
|
||||
Details = ex.Message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens and configures a serial port for communication
|
||||
/// </summary>
|
||||
/// <param name="request">Port configuration parameters</param>
|
||||
/// <returns>Result of the port open operation</returns>
|
||||
/// <response code="200">Port opened successfully</response>
|
||||
/// <response code="400">Invalid request parameters</response>
|
||||
/// <response code="500">Internal server error</response>
|
||||
[HttpPost("open")]
|
||||
[ProducesResponseType(typeof(PortOperationResponse), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status500InternalServerError)]
|
||||
public async Task<ActionResult<PortOperationResponse>> OpenPort([FromBody] OpenPortRequest request)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(new ErrorResponse
|
||||
{
|
||||
Error = "Invalid request parameters",
|
||||
Details = string.Join("; ", ModelState.Values.SelectMany(v => v.Errors).Select(e => e.ErrorMessage))
|
||||
});
|
||||
}
|
||||
|
||||
_logger.LogInformation("Attempting to open port {PortName} with baud rate {BaudRate}",
|
||||
request.PortName, request.BaudRate);
|
||||
|
||||
var success = await _serialService.OpenPortAsync(
|
||||
request.PortName,
|
||||
request.BaudRate,
|
||||
request.Parity,
|
||||
request.StopBits,
|
||||
request.Handshake);
|
||||
|
||||
var response = new PortOperationResponse
|
||||
{
|
||||
Success = success,
|
||||
PortName = request.PortName,
|
||||
IsOpen = _serialService.IsPortOpen(),
|
||||
Message = success
|
||||
? $"Port {request.PortName} opened successfully"
|
||||
: $"Failed to open port {request.PortName}"
|
||||
};
|
||||
|
||||
if (success)
|
||||
{
|
||||
_logger.LogInformation("Successfully opened port {PortName}", request.PortName);
|
||||
return Ok(response);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning("Failed to open port {PortName}", request.PortName);
|
||||
return StatusCode(500, new ErrorResponse
|
||||
{
|
||||
Error = response.Message
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (ArgumentException ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Invalid argument for opening port: {PortName}", request.PortName);
|
||||
return BadRequest(new ErrorResponse
|
||||
{
|
||||
Error = "Invalid port parameters",
|
||||
Details = ex.Message
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error opening port: {PortName}", request.PortName);
|
||||
return StatusCode(500, new ErrorResponse
|
||||
{
|
||||
Error = "Internal server error while opening port",
|
||||
Details = ex.Message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes the currently opened serial port
|
||||
/// </summary>
|
||||
/// <returns>Result of the port close operation</returns>
|
||||
/// <response code="200">Port closed successfully</response>
|
||||
/// <response code="500">Internal server error</response>
|
||||
[HttpPost("close")]
|
||||
[ProducesResponseType(typeof(PortOperationResponse), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status500InternalServerError)]
|
||||
public async Task<ActionResult<PortOperationResponse>> ClosePort()
|
||||
{
|
||||
try
|
||||
{
|
||||
var currentPortName = _serialService.GetCurrentPortName();
|
||||
_logger.LogInformation("Attempting to close port {PortName}", currentPortName ?? "unknown");
|
||||
|
||||
await _serialService.ClosePortAsync();
|
||||
|
||||
var response = new PortOperationResponse
|
||||
{
|
||||
Success = true,
|
||||
PortName = currentPortName,
|
||||
IsOpen = _serialService.IsPortOpen(),
|
||||
Message = "Port closed successfully"
|
||||
};
|
||||
|
||||
_logger.LogInformation("Successfully closed port");
|
||||
return Ok(response);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error closing port");
|
||||
return StatusCode(500, new ErrorResponse
|
||||
{
|
||||
Error = "Internal server error while closing port",
|
||||
Details = ex.Message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a CAT command to the connected radio
|
||||
/// </summary>
|
||||
/// <param name="request">CAT command to send</param>
|
||||
/// <returns>Result of the command operation including any response from the radio</returns>
|
||||
/// <response code="200">Command sent successfully</response>
|
||||
/// <response code="400">Invalid request parameters or no port open</response>
|
||||
/// <response code="500">Internal server error</response>
|
||||
[HttpPost("command")]
|
||||
[ProducesResponseType(typeof(CommandResponse), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status500InternalServerError)]
|
||||
public async Task<ActionResult<CommandResponse>> SendCommand([FromBody] SendCommandRequest request)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(new ErrorResponse
|
||||
{
|
||||
Error = "Invalid request parameters",
|
||||
Details = string.Join("; ", ModelState.Values.SelectMany(v => v.Errors).Select(e => e.ErrorMessage))
|
||||
});
|
||||
}
|
||||
|
||||
if (!_serialService.IsPortOpen())
|
||||
{
|
||||
return BadRequest(new ErrorResponse
|
||||
{
|
||||
Error = "No serial port is currently open",
|
||||
Details = "You must open a serial port before sending commands"
|
||||
});
|
||||
}
|
||||
|
||||
_logger.LogInformation("Sending CAT command: {Command}", request.Command);
|
||||
|
||||
var radioResponse = await _serialService.SendCommandAsync(request.Command);
|
||||
|
||||
var response = new CommandResponse
|
||||
{
|
||||
Success = true,
|
||||
Command = request.Command,
|
||||
Response = radioResponse,
|
||||
Message = "Command sent successfully",
|
||||
Timestamp = DateTime.UtcNow
|
||||
};
|
||||
|
||||
_logger.LogInformation("CAT command sent successfully. Response: {Response}", radioResponse);
|
||||
return Ok(response);
|
||||
}
|
||||
catch (ArgumentException ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Invalid command: {Command}", request.Command);
|
||||
return BadRequest(new ErrorResponse
|
||||
{
|
||||
Error = "Invalid command",
|
||||
Details = ex.Message
|
||||
});
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Cannot send command - port not open");
|
||||
return BadRequest(new ErrorResponse
|
||||
{
|
||||
Error = "Cannot send command",
|
||||
Details = ex.Message
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error sending CAT command: {Command}", request.Command);
|
||||
return StatusCode(500, new ErrorResponse
|
||||
{
|
||||
Error = "Internal server error while sending command",
|
||||
Details = ex.Message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current status of the serial port connection
|
||||
/// </summary>
|
||||
/// <returns>Current port status</returns>
|
||||
/// <response code="200">Returns current port status</response>
|
||||
[HttpGet("status")]
|
||||
[ProducesResponseType(typeof(PortOperationResponse), StatusCodes.Status200OK)]
|
||||
public ActionResult<PortOperationResponse> GetStatus()
|
||||
{
|
||||
try
|
||||
{
|
||||
var isOpen = _serialService.IsPortOpen();
|
||||
var currentPort = _serialService.GetCurrentPortName();
|
||||
|
||||
var response = new PortOperationResponse
|
||||
{
|
||||
Success = true,
|
||||
PortName = currentPort,
|
||||
IsOpen = isOpen,
|
||||
Message = isOpen
|
||||
? $"Port {currentPort} is open"
|
||||
: "No port is currently open"
|
||||
};
|
||||
|
||||
return Ok(response);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error getting port status");
|
||||
return StatusCode(500, new ErrorResponse
|
||||
{
|
||||
Error = "Internal server error while getting status",
|
||||
Details = ex.Message
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
59
Server/Controllers/SerialController.cs
Normal file
59
Server/Controllers/SerialController.cs
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using Server.Models;
|
||||
using Server.Services;
|
||||
|
||||
namespace Server.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// Controller for serial port management operations
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
[Produces("application/json")]
|
||||
public class SerialController : ControllerBase
|
||||
{
|
||||
private readonly ISerialCommunicationService _serialService;
|
||||
private readonly ILogger<SerialController> _logger;
|
||||
|
||||
public SerialController(ISerialCommunicationService serialService, ILogger<SerialController> logger)
|
||||
{
|
||||
_serialService = serialService ?? throw new ArgumentNullException(nameof(serialService));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all available serial ports on the server
|
||||
/// </summary>
|
||||
/// <returns>JSON array of available serial port names</returns>
|
||||
/// <response code="200">Returns the list of available serial ports</response>
|
||||
/// <response code="500">Internal server error</response>
|
||||
[HttpGet("ports")]
|
||||
[ProducesResponseType(typeof(PortListResponse), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status500InternalServerError)]
|
||||
public ActionResult<PortListResponse> GetPorts()
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("Getting available serial ports");
|
||||
var ports = _serialService.GetAvailablePorts();
|
||||
|
||||
var response = new PortListResponse
|
||||
{
|
||||
Ports = ports
|
||||
};
|
||||
|
||||
_logger.LogInformation("Found {PortCount} available ports", response.Count);
|
||||
return Ok(response);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error getting available ports");
|
||||
return StatusCode(500, new ErrorResponse
|
||||
{
|
||||
Error = "Internal server error while getting ports",
|
||||
Details = ex.Message
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
143
Server/Models/ApiModels.cs
Normal file
143
Server/Models/ApiModels.cs
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using System.IO.Ports;
|
||||
|
||||
namespace Server.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Response model for listing available serial ports
|
||||
/// </summary>
|
||||
public class PortListResponse
|
||||
{
|
||||
/// <summary>
|
||||
/// Array of available serial port names
|
||||
/// </summary>
|
||||
public string[] Ports { get; set; } = Array.Empty<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Number of available ports
|
||||
/// </summary>
|
||||
public int Count => Ports.Length;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Request model for opening a serial port
|
||||
/// </summary>
|
||||
public class OpenPortRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// Name of the serial port to open (e.g., "COM1", "/dev/ttyUSB0")
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string PortName { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Baud rate for communication (default: 9600)
|
||||
/// </summary>
|
||||
public int BaudRate { get; set; } = 9600;
|
||||
|
||||
/// <summary>
|
||||
/// Parity setting (default: None)
|
||||
/// </summary>
|
||||
public Parity Parity { get; set; } = Parity.None;
|
||||
|
||||
/// <summary>
|
||||
/// Stop bits setting (default: One)
|
||||
/// </summary>
|
||||
public StopBits StopBits { get; set; } = StopBits.One;
|
||||
|
||||
/// <summary>
|
||||
/// Handshake setting (default: None)
|
||||
/// </summary>
|
||||
public Handshake Handshake { get; set; } = Handshake.None;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Response model for port operations
|
||||
/// </summary>
|
||||
public class PortOperationResponse
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates if the operation was successful
|
||||
/// </summary>
|
||||
public bool Success { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Human-readable message about the operation
|
||||
/// </summary>
|
||||
public string Message { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Name of the port that was operated on
|
||||
/// </summary>
|
||||
public string? PortName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Current status of the port (open/closed)
|
||||
/// </summary>
|
||||
public bool IsOpen { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Request model for sending CAT commands
|
||||
/// </summary>
|
||||
public class SendCommandRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// CAT command string to send to the radio
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string Command { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Response model for CAT command operations
|
||||
/// </summary>
|
||||
public class CommandResponse
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates if the command was sent successfully
|
||||
/// </summary>
|
||||
public bool Success { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The command that was sent
|
||||
/// </summary>
|
||||
public string Command { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Response received from the radio (if any)
|
||||
/// </summary>
|
||||
public string? Response { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Human-readable message about the operation
|
||||
/// </summary>
|
||||
public string Message { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Timestamp when the command was executed
|
||||
/// </summary>
|
||||
public DateTime Timestamp { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Response model for errors
|
||||
/// </summary>
|
||||
public class ErrorResponse
|
||||
{
|
||||
/// <summary>
|
||||
/// Error message
|
||||
/// </summary>
|
||||
public string Error { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Additional details about the error
|
||||
/// </summary>
|
||||
public string? Details { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Timestamp when the error occurred
|
||||
/// </summary>
|
||||
public DateTime Timestamp { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
67
Server/Program.cs
Normal file
67
Server/Program.cs
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
using Server.Services;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Add services to the container
|
||||
builder.Services.AddControllers();
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
builder.Services.AddSwaggerGen(c =>
|
||||
{
|
||||
c.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo
|
||||
{
|
||||
Title = "SharpCAT Server API",
|
||||
Version = "v1",
|
||||
Description = "ASP.NET Core Web API for CAT (Computer Aided Transceiver) control using SharpCAT library",
|
||||
Contact = new Microsoft.OpenApi.Models.OpenApiContact
|
||||
{
|
||||
Name = "SharpCAT Project"
|
||||
}
|
||||
});
|
||||
|
||||
// Include XML comments if available
|
||||
var xmlFilename = $"{System.Reflection.Assembly.GetExecutingAssembly().GetName().Name}.xml";
|
||||
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFilename);
|
||||
if (File.Exists(xmlPath))
|
||||
{
|
||||
c.IncludeXmlComments(xmlPath);
|
||||
}
|
||||
});
|
||||
|
||||
// Register the serial communication service as a singleton
|
||||
// to maintain connection state across requests
|
||||
builder.Services.AddSingleton<ISerialCommunicationService, SerialCommunicationService>();
|
||||
|
||||
// Add CORS policy for cross-origin requests
|
||||
builder.Services.AddCors(options =>
|
||||
{
|
||||
options.AddPolicy("AllowAll", policy =>
|
||||
{
|
||||
policy.AllowAnyOrigin()
|
||||
.AllowAnyMethod()
|
||||
.AllowAnyHeader();
|
||||
});
|
||||
});
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// Configure the HTTP request pipeline
|
||||
if (app.Environment.IsDevelopment())
|
||||
{
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI(c =>
|
||||
{
|
||||
c.SwaggerEndpoint("/swagger/v1/swagger.json", "SharpCAT Server API v1");
|
||||
c.RoutePrefix = string.Empty; // Set Swagger UI at app's root
|
||||
});
|
||||
}
|
||||
|
||||
app.UseCors("AllowAll");
|
||||
app.UseRouting();
|
||||
app.MapControllers();
|
||||
|
||||
// Add a simple health check endpoint
|
||||
app.MapGet("/health", () => new { status = "healthy", timestamp = DateTime.UtcNow })
|
||||
.WithName("HealthCheck")
|
||||
.WithOpenApi();
|
||||
|
||||
app.Run();
|
||||
31
Server/Properties/launchSettings.json
Normal file
31
Server/Properties/launchSettings.json
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:14425",
|
||||
"sslPort": 0
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
"http": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"applicationUrl": "http://localhost:5188",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
150
Server/README.md
Normal file
150
Server/README.md
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
# SharpCAT Server
|
||||
|
||||
A cross-platform ASP.NET Core Web API server that provides REST endpoints for CAT (Computer Aided Transceiver) control using the SharpCAT library.
|
||||
|
||||
## Features
|
||||
|
||||
- **Cross-platform**: Works on Windows, Linux, and macOS
|
||||
- **REST API**: Clean, documented REST endpoints for radio control
|
||||
- **Serial Communication**: Leverages the SharpCAT core library for serial port operations
|
||||
- **Swagger Documentation**: Built-in API documentation and testing interface
|
||||
- **Error Handling**: Comprehensive error handling and logging
|
||||
|
||||
## API Endpoints
|
||||
|
||||
### Serial Port Management
|
||||
|
||||
- `GET /api/cat/ports` - List all available serial ports
|
||||
- `POST /api/cat/open` - Open and configure a serial port
|
||||
- `POST /api/cat/close` - Close the currently opened port
|
||||
- `GET /api/cat/status` - Get current port connection status
|
||||
|
||||
### CAT Commands
|
||||
|
||||
- `POST /api/cat/command` - Send arbitrary CAT commands to the radio
|
||||
|
||||
### Health Check
|
||||
|
||||
- `GET /health` - Simple health check endpoint
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- [.NET 8.0 SDK](https://dotnet.microsoft.com/download) or later
|
||||
|
||||
### Running the Server
|
||||
|
||||
1. Navigate to the server directory:
|
||||
```bash
|
||||
cd Server/SharpCAT.Server
|
||||
```
|
||||
|
||||
2. Build and run the server:
|
||||
```bash
|
||||
dotnet run
|
||||
```
|
||||
|
||||
3. The server will start and listen on `http://localhost:5188` by default
|
||||
|
||||
4. Open your browser to `http://localhost:5188` to access the Swagger UI for interactive API documentation
|
||||
|
||||
### Example Usage
|
||||
|
||||
#### List Available Ports
|
||||
```bash
|
||||
curl http://localhost:5188/api/cat/ports
|
||||
```
|
||||
|
||||
#### Open a Serial Port
|
||||
```bash
|
||||
curl -X POST http://localhost:5188/api/cat/open \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"portName": "COM3",
|
||||
"baudRate": 9600,
|
||||
"parity": "None",
|
||||
"stopBits": "One",
|
||||
"handshake": "None"
|
||||
}'
|
||||
```
|
||||
|
||||
#### Send a CAT Command
|
||||
```bash
|
||||
curl -X POST http://localhost:5188/api/cat/command \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"command": "FA;"}'
|
||||
```
|
||||
|
||||
#### Check Status
|
||||
```bash
|
||||
curl http://localhost:5188/api/cat/status
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
### Port Configuration Options
|
||||
|
||||
When opening a port, you can configure:
|
||||
|
||||
- **Port Name**: Serial port name (e.g., "COM1" on Windows, "/dev/ttyUSB0" on Linux)
|
||||
- **Baud Rate**: Communication speed (default: 9600)
|
||||
- **Parity**: Error checking method (None, Odd, Even, Mark, Space)
|
||||
- **Stop Bits**: Number of stop bits (None, One, Two, OnePointFive)
|
||||
- **Handshake**: Flow control method (None, XOnXOff, RequestToSend, RequestToSendXOnXOff)
|
||||
|
||||
### Logging
|
||||
|
||||
The server uses ASP.NET Core's built-in logging. Log levels can be configured in `appsettings.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"SharpCAT.Server": "Debug"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Development
|
||||
|
||||
### Building from Source
|
||||
|
||||
1. Ensure you have the .NET 8.0 SDK installed
|
||||
2. Clone the repository
|
||||
3. Navigate to the project root
|
||||
4. Build the solution:
|
||||
```bash
|
||||
dotnet build
|
||||
```
|
||||
|
||||
### Project Structure
|
||||
|
||||
```
|
||||
Server/SharpCAT.Server/
|
||||
├── Controllers/
|
||||
│ └── CatController.cs # Main API controller
|
||||
├── Models/
|
||||
│ └── ApiModels.cs # Request/response models
|
||||
├── Services/
|
||||
│ ├── ISerialCommunicationService.cs # Service interface
|
||||
│ └── SerialCommunicationService.cs # Service implementation
|
||||
├── Program.cs # Application entry point
|
||||
└── SharpCAT.Server.csproj # Project file
|
||||
```
|
||||
|
||||
### Dependencies
|
||||
|
||||
- **Microsoft.AspNetCore.OpenApi** - OpenAPI support
|
||||
- **Swashbuckle.AspNetCore** - Swagger documentation
|
||||
- **SharpCAT** - Core CAT control library
|
||||
|
||||
## License
|
||||
|
||||
This project follows the same license as the parent SharpCAT project.
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are welcome! Please follow the existing code style and add appropriate tests for new features.
|
||||
20
Server/Server.csproj
Normal file
20
Server/Server.csproj
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<NoWarn>$(NoWarn);1591</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.18" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Library\Library.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
51
Server/Services/ISerialCommunicationService.cs
Normal file
51
Server/Services/ISerialCommunicationService.cs
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
using System.IO.Ports;
|
||||
|
||||
namespace Server.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface for serial communication services
|
||||
/// </summary>
|
||||
public interface ISerialCommunicationService
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets all available serial ports on the system
|
||||
/// </summary>
|
||||
/// <returns>Array of port names</returns>
|
||||
string[] GetAvailablePorts();
|
||||
|
||||
/// <summary>
|
||||
/// Opens and configures a serial port
|
||||
/// </summary>
|
||||
/// <param name="portName">Name of the port to open</param>
|
||||
/// <param name="baudRate">Baud rate for communication</param>
|
||||
/// <param name="parity">Parity setting</param>
|
||||
/// <param name="stopBits">Stop bits setting</param>
|
||||
/// <param name="handshake">Handshake setting</param>
|
||||
/// <returns>True if port opened successfully</returns>
|
||||
Task<bool> OpenPortAsync(string portName, int baudRate, Parity parity, StopBits stopBits, Handshake handshake);
|
||||
|
||||
/// <summary>
|
||||
/// Closes the currently opened port
|
||||
/// </summary>
|
||||
Task ClosePortAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Sends a CAT command to the radio
|
||||
/// </summary>
|
||||
/// <param name="command">CAT command string to send</param>
|
||||
/// <returns>Response from the radio, if any</returns>
|
||||
Task<string> SendCommandAsync(string command);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the status of the current port connection
|
||||
/// </summary>
|
||||
/// <returns>True if port is open and connected</returns>
|
||||
bool IsPortOpen();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the currently opened port
|
||||
/// </summary>
|
||||
/// <returns>Port name or null if no port is open</returns>
|
||||
string? GetCurrentPortName();
|
||||
}
|
||||
}
|
||||
208
Server/Services/SerialCommunicationService.cs
Normal file
208
Server/Services/SerialCommunicationService.cs
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
using System.IO.Ports;
|
||||
using System.Text;
|
||||
|
||||
namespace Server.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Service for managing serial communication with radios using SharpCAT library
|
||||
/// </summary>
|
||||
public class SerialCommunicationService : ISerialCommunicationService, IDisposable
|
||||
{
|
||||
private readonly ILogger<SerialCommunicationService> _logger;
|
||||
private SharpCAT.Serial? _serialConnection;
|
||||
private SerialPort? _directSerialPort;
|
||||
private string? _currentPortName;
|
||||
private readonly SemaphoreSlim _semaphore = new(1, 1);
|
||||
private bool _disposed = false;
|
||||
|
||||
public SerialCommunicationService(ILogger<SerialCommunicationService> logger)
|
||||
{
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string[] GetAvailablePorts()
|
||||
{
|
||||
try
|
||||
{
|
||||
var sharpCat = new SharpCAT.SharpCAT();
|
||||
var ports = sharpCat.PortNames;
|
||||
_logger.LogInformation("Found {PortCount} available serial ports", ports?.Length ?? 0);
|
||||
return ports ?? Array.Empty<string>();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error getting available serial ports");
|
||||
return Array.Empty<string>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<bool> OpenPortAsync(string portName, int baudRate, Parity parity, StopBits stopBits, Handshake handshake)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(portName))
|
||||
throw new ArgumentException("Port name cannot be null or empty", nameof(portName));
|
||||
|
||||
await _semaphore.WaitAsync();
|
||||
try
|
||||
{
|
||||
// Close existing connection if any
|
||||
await ClosePortInternalAsync();
|
||||
|
||||
_logger.LogInformation("Attempting to open port {PortName} with baud rate {BaudRate}", portName, baudRate);
|
||||
|
||||
// Create direct SerialPort for more control over communication
|
||||
_directSerialPort = new SerialPort
|
||||
{
|
||||
PortName = portName,
|
||||
BaudRate = baudRate,
|
||||
Parity = parity,
|
||||
StopBits = stopBits,
|
||||
Handshake = handshake,
|
||||
DataBits = 8,
|
||||
ReadTimeout = 5000,
|
||||
WriteTimeout = 5000,
|
||||
Encoding = Encoding.ASCII
|
||||
};
|
||||
|
||||
_directSerialPort.Open();
|
||||
_currentPortName = portName;
|
||||
|
||||
_logger.LogInformation("Successfully opened port {PortName}", portName);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to open port {PortName}", portName);
|
||||
_directSerialPort?.Dispose();
|
||||
_directSerialPort = null;
|
||||
_currentPortName = null;
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_semaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task ClosePortAsync()
|
||||
{
|
||||
await _semaphore.WaitAsync();
|
||||
try
|
||||
{
|
||||
await ClosePortInternalAsync();
|
||||
}
|
||||
finally
|
||||
{
|
||||
_semaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<string> SendCommandAsync(string command)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(command))
|
||||
throw new ArgumentException("Command cannot be null or empty", nameof(command));
|
||||
|
||||
await _semaphore.WaitAsync();
|
||||
try
|
||||
{
|
||||
if (_directSerialPort == null || !_directSerialPort.IsOpen)
|
||||
{
|
||||
throw new InvalidOperationException("No serial port is currently open");
|
||||
}
|
||||
|
||||
_logger.LogDebug("Sending command: {Command}", command);
|
||||
|
||||
// Send the command
|
||||
await Task.Run(() => _directSerialPort.WriteLine(command));
|
||||
|
||||
// Wait a bit for response
|
||||
await Task.Delay(100);
|
||||
|
||||
// Try to read response
|
||||
string response = "";
|
||||
if (_directSerialPort.BytesToRead > 0)
|
||||
{
|
||||
response = await Task.Run(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
return _directSerialPort.ReadExisting();
|
||||
}
|
||||
catch (TimeoutException)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_logger.LogDebug("Command response: {Response}", response);
|
||||
return response.Trim();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error sending command: {Command}", command);
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_semaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsPortOpen()
|
||||
{
|
||||
return _directSerialPort?.IsOpen == true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string? GetCurrentPortName()
|
||||
{
|
||||
return _currentPortName;
|
||||
}
|
||||
|
||||
private async Task ClosePortInternalAsync()
|
||||
{
|
||||
if (_directSerialPort != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_directSerialPort.IsOpen)
|
||||
{
|
||||
await Task.Run(() => _directSerialPort.Close());
|
||||
}
|
||||
_directSerialPort.Dispose();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Error closing serial port");
|
||||
}
|
||||
finally
|
||||
{
|
||||
_directSerialPort = null;
|
||||
_currentPortName = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (_serialConnection != null)
|
||||
{
|
||||
_serialConnection = null;
|
||||
}
|
||||
|
||||
_logger.LogInformation("Serial port closed");
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
ClosePortInternalAsync().Wait();
|
||||
_semaphore?.Dispose();
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
6
Server/SharpCAT.Server.http
Normal file
6
Server/SharpCAT.Server.http
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
@SharpCAT.Server_HostAddress = http://localhost:5188
|
||||
|
||||
GET {{SharpCAT.Server_HostAddress}}/weatherforecast/
|
||||
Accept: application/json
|
||||
|
||||
###
|
||||
8
Server/appsettings.Development.json
Normal file
8
Server/appsettings.Development.json
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
||||
9
Server/appsettings.json
Normal file
9
Server/appsettings.json
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
||||
39
SharpCAT.sln
Normal file
39
SharpCAT.sln
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.0.31903.59
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Library", "Library\Library.csproj", "{3EA807EF-B181-4C54-8502-0A2A3EACE984}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Server", "Server", "{43CFF66C-84E6-4EC2-AE4F-005FB80D74D5}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Server", "Server\Server.csproj", "{22E4F655-252E-42DB-ADD5-494BC825A97C}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpCAT.Client", "Client\SharpCAT.Client.csproj", "{661C070A-612A-41CD-B9DC-9EF9FA05121A}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{3EA807EF-B181-4C54-8502-0A2A3EACE984}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3EA807EF-B181-4C54-8502-0A2A3EACE984}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3EA807EF-B181-4C54-8502-0A2A3EACE984}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3EA807EF-B181-4C54-8502-0A2A3EACE984}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{22E4F655-252E-42DB-ADD5-494BC825A97C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{22E4F655-252E-42DB-ADD5-494BC825A97C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{22E4F655-252E-42DB-ADD5-494BC825A97C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{22E4F655-252E-42DB-ADD5-494BC825A97C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{661C070A-612A-41CD-B9DC-9EF9FA05121A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{661C070A-612A-41CD-B9DC-9EF9FA05121A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{661C070A-612A-41CD-B9DC-9EF9FA05121A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{661C070A-612A-41CD-B9DC-9EF9FA05121A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{22E4F655-252E-42DB-ADD5-494BC825A97C} = {43CFF66C-84E6-4EC2-AE4F-005FB80D74D5}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
|
@ -1,187 +0,0 @@
|
|||
<?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>{65A037CB-9245-442A-A791-5CFC34E97BF7}</ProjectGuid>
|
||||
<RootNamespace>$(MSBuildProjectName)</RootNamespace>
|
||||
<UMDF_VERSION_MAJOR>2</UMDF_VERSION_MAJOR>
|
||||
<Configuration Condition="'$(Configuration)' == ''">Debug</Configuration>
|
||||
<Platform Condition="'$(Platform)' == ''">Win32</Platform>
|
||||
<SampleGuid>{D3E77C62-C6F0-4210-824E-1875C7B48EC5}</SampleGuid>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<TargetVersion>Windows10</TargetVersion>
|
||||
<UseDebugLibraries>False</UseDebugLibraries>
|
||||
<DriverTargetPlatform>Universal</DriverTargetPlatform>
|
||||
<DriverType>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>Universal</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>Universal</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>Universal</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" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<TargetName>VirtualSerial2um</TargetName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<TargetName>VirtualSerial2um</TargetName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<TargetName>VirtualSerial2um</TargetName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<TargetName>VirtualSerial2um</TargetName>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>%(PreprocessorDefinitions);_UNICODE;UNICODE</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)\wdm;..\..\inc</AdditionalIncludeDirectories>
|
||||
<ExceptionHandling>
|
||||
</ExceptionHandling>
|
||||
</ClCompile>
|
||||
<Midl>
|
||||
<PreprocessorDefinitions>%(PreprocessorDefinitions);_UNICODE;UNICODE</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)\wdm;..\..\inc</AdditionalIncludeDirectories>
|
||||
</Midl>
|
||||
<ResourceCompile>
|
||||
<PreprocessorDefinitions>%(PreprocessorDefinitions);_UNICODE;UNICODE</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)\wdm;..\..\inc</AdditionalIncludeDirectories>
|
||||
</ResourceCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>%(PreprocessorDefinitions);_UNICODE;UNICODE</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)\wdm;..\..\inc</AdditionalIncludeDirectories>
|
||||
<ExceptionHandling>
|
||||
</ExceptionHandling>
|
||||
</ClCompile>
|
||||
<Midl>
|
||||
<PreprocessorDefinitions>%(PreprocessorDefinitions);_UNICODE;UNICODE</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)\wdm;..\..\inc</AdditionalIncludeDirectories>
|
||||
</Midl>
|
||||
<ResourceCompile>
|
||||
<PreprocessorDefinitions>%(PreprocessorDefinitions);_UNICODE;UNICODE</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)\wdm;..\..\inc</AdditionalIncludeDirectories>
|
||||
</ResourceCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>%(PreprocessorDefinitions);_UNICODE;UNICODE</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)\wdm;..\..\inc</AdditionalIncludeDirectories>
|
||||
<ExceptionHandling>
|
||||
</ExceptionHandling>
|
||||
</ClCompile>
|
||||
<Midl>
|
||||
<PreprocessorDefinitions>%(PreprocessorDefinitions);_UNICODE;UNICODE</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)\wdm;..\..\inc</AdditionalIncludeDirectories>
|
||||
</Midl>
|
||||
<ResourceCompile>
|
||||
<PreprocessorDefinitions>%(PreprocessorDefinitions);_UNICODE;UNICODE</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)\wdm;..\..\inc</AdditionalIncludeDirectories>
|
||||
</ResourceCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>%(PreprocessorDefinitions);_UNICODE;UNICODE</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)\wdm;..\..\inc</AdditionalIncludeDirectories>
|
||||
<ExceptionHandling>
|
||||
</ExceptionHandling>
|
||||
</ClCompile>
|
||||
<Midl>
|
||||
<PreprocessorDefinitions>%(PreprocessorDefinitions);_UNICODE;UNICODE</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)\wdm;..\..\inc</AdditionalIncludeDirectories>
|
||||
</Midl>
|
||||
<ResourceCompile>
|
||||
<PreprocessorDefinitions>%(PreprocessorDefinitions);_UNICODE;UNICODE</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)\wdm;..\..\inc</AdditionalIncludeDirectories>
|
||||
</ResourceCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\device.c" />
|
||||
<ClCompile Include="..\driver.c" />
|
||||
<ClCompile Include="..\queue.c" />
|
||||
<ClCompile Include="..\ringbuffer.c" />
|
||||
<ResourceCompile Include="virtualserial2um.rc" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Inf Exclude="@(Inf)" Include="*.inx" />
|
||||
<FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" />
|
||||
</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>
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
<?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>{75562F82-D2DC-4C96-8D1E-01C195D61A2C}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||
<UniqueIdentifier>{0748F304-5FE6-4C01-B27A-3CD7B1A268B7}</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>{87F14B94-F7A4-42E8-9931-8ADDA3989EBD}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Driver Files">
|
||||
<Extensions>inf;inv;inx;mof;mc;</Extensions>
|
||||
<UniqueIdentifier>{30F678DB-C12C-461E-8128-CE409AAC9C42}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\device.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\driver.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\queue.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\ringbuffer.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="virtualserial2um.rc">
|
||||
<Filter>Resource Files</Filter>
|
||||
</ResourceCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
Binary file not shown.
|
|
@ -1,17 +0,0 @@
|
|||
//---------------------------------------------------------------------------
|
||||
// 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 v2 Driver Sample"
|
||||
#define VER_INTERNALNAME_STR "VirtualSerial2um"
|
||||
#define VER_ORIGINALFILENAME_STR "Virtualserial2um.dll"
|
||||
|
||||
#include "common.ver"
|
||||
Binary file not shown.
|
|
@ -1,17 +0,0 @@
|
|||
//---------------------------------------------------------------------------
|
||||
// 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 "FakeModem2um"
|
||||
#define VER_ORIGINALFILENAME_STR "FakeModem2um.dll"
|
||||
|
||||
#include "common.ver"
|
||||
|
|
@ -1,187 +0,0 @@
|
|||
<?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>{CD78D78F-B132-4E6F-A11F-B62185A6152A}</ProjectGuid>
|
||||
<RootNamespace>$(MSBuildProjectName)</RootNamespace>
|
||||
<UMDF_VERSION_MAJOR>2</UMDF_VERSION_MAJOR>
|
||||
<Configuration Condition="'$(Configuration)' == ''">Debug</Configuration>
|
||||
<Platform Condition="'$(Platform)' == ''">Win32</Platform>
|
||||
<SampleGuid>{3E00ED06-5DB5-444F-8FFA-D098A0218DF2}</SampleGuid>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<TargetVersion>Windows10</TargetVersion>
|
||||
<UseDebugLibraries>False</UseDebugLibraries>
|
||||
<DriverTargetPlatform>Universal</DriverTargetPlatform>
|
||||
<DriverType>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>Universal</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>Universal</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>Universal</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" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<TargetName>fakemodem2um</TargetName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<TargetName>fakemodem2um</TargetName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<TargetName>fakemodem2um</TargetName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<TargetName>fakemodem2um</TargetName>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>%(PreprocessorDefinitions);_UNICODE;UNICODE;_FAKE_MODEM=1</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)\wdm;..\..\inc</AdditionalIncludeDirectories>
|
||||
<ExceptionHandling>
|
||||
</ExceptionHandling>
|
||||
</ClCompile>
|
||||
<Midl>
|
||||
<PreprocessorDefinitions>%(PreprocessorDefinitions);_UNICODE;UNICODE;_FAKE_MODEM=1</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)\wdm;..\..\inc</AdditionalIncludeDirectories>
|
||||
</Midl>
|
||||
<ResourceCompile>
|
||||
<PreprocessorDefinitions>%(PreprocessorDefinitions);_UNICODE;UNICODE;_FAKE_MODEM=1</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)\wdm;..\..\inc</AdditionalIncludeDirectories>
|
||||
</ResourceCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>%(PreprocessorDefinitions);_UNICODE;UNICODE;_FAKE_MODEM=1</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)\wdm;..\..\inc</AdditionalIncludeDirectories>
|
||||
<ExceptionHandling>
|
||||
</ExceptionHandling>
|
||||
</ClCompile>
|
||||
<Midl>
|
||||
<PreprocessorDefinitions>%(PreprocessorDefinitions);_UNICODE;UNICODE;_FAKE_MODEM=1</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)\wdm;..\..\inc</AdditionalIncludeDirectories>
|
||||
</Midl>
|
||||
<ResourceCompile>
|
||||
<PreprocessorDefinitions>%(PreprocessorDefinitions);_UNICODE;UNICODE;_FAKE_MODEM=1</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)\wdm;..\..\inc</AdditionalIncludeDirectories>
|
||||
</ResourceCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>%(PreprocessorDefinitions);_UNICODE;UNICODE;_FAKE_MODEM=1</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)\wdm;..\..\inc</AdditionalIncludeDirectories>
|
||||
<ExceptionHandling>
|
||||
</ExceptionHandling>
|
||||
</ClCompile>
|
||||
<Midl>
|
||||
<PreprocessorDefinitions>%(PreprocessorDefinitions);_UNICODE;UNICODE;_FAKE_MODEM=1</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)\wdm;..\..\inc</AdditionalIncludeDirectories>
|
||||
</Midl>
|
||||
<ResourceCompile>
|
||||
<PreprocessorDefinitions>%(PreprocessorDefinitions);_UNICODE;UNICODE;_FAKE_MODEM=1</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)\wdm;..\..\inc</AdditionalIncludeDirectories>
|
||||
</ResourceCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>%(PreprocessorDefinitions);_UNICODE;UNICODE;_FAKE_MODEM=1</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)\wdm;..\..\inc</AdditionalIncludeDirectories>
|
||||
<ExceptionHandling>
|
||||
</ExceptionHandling>
|
||||
</ClCompile>
|
||||
<Midl>
|
||||
<PreprocessorDefinitions>%(PreprocessorDefinitions);_UNICODE;UNICODE;_FAKE_MODEM=1</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)\wdm;..\..\inc</AdditionalIncludeDirectories>
|
||||
</Midl>
|
||||
<ResourceCompile>
|
||||
<PreprocessorDefinitions>%(PreprocessorDefinitions);_UNICODE;UNICODE;_FAKE_MODEM=1</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)\wdm;..\..\inc</AdditionalIncludeDirectories>
|
||||
</ResourceCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\device.c" />
|
||||
<ClCompile Include="..\driver.c" />
|
||||
<ClCompile Include="..\queue.c" />
|
||||
<ClCompile Include="..\ringbuffer.c" />
|
||||
<ResourceCompile Include="fakemodem2um.rc" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Inf Exclude="@(Inf)" Include="*.inx" />
|
||||
<FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" />
|
||||
</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>
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
<?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>{5A926099-4361-4BDC-BF0F-D98AD6F55C3A}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||
<UniqueIdentifier>{9C5FC831-BCE8-4E84-8BED-0B1182AA7358}</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>{43B83C13-0033-4A5E-A0DB-F0BA1B304FB0}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Driver Files">
|
||||
<Extensions>inf;inv;inx;mof;mc;</Extensions>
|
||||
<UniqueIdentifier>{83A3125A-7FA1-4416-B24A-FA9C1EECAAF0}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\device.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\driver.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\queue.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\ringbuffer.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="fakemodem2um.rc">
|
||||
<Filter>Resource Files</Filter>
|
||||
</ResourceCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
---
|
||||
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.
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
|
||||
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
|
||||
|
|
@ -1,449 +0,0 @@
|
|||
/*++
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
@ -1,122 +0,0 @@
|
|||
/*++
|
||||
|
||||
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
|
||||
);
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
/*++
|
||||
|
||||
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 <initguid.h>
|
||||
#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;
|
||||
}
|
||||
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
/*++
|
||||
|
||||
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;
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
/*++
|
||||
|
||||
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 <ntddk.h>
|
||||
#else
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include <wdf.h>
|
||||
|
||||
#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
|
||||
|
|
@ -1,963 +0,0 @@
|
|||
/*++
|
||||
|
||||
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<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.
|
||||
--*/
|
||||
{
|
||||
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 <cr><lf>CONNECT<cr><lf> 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 <cr><lf>OK<cr><lf> 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;
|
||||
}
|
||||
|
|
@ -1,113 +0,0 @@
|
|||
/*++
|
||||
|
||||
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
|
||||
);
|
||||
|
||||
|
|
@ -1,338 +0,0 @@
|
|||
/*++
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -1,117 +0,0 @@
|
|||
/*++
|
||||
|
||||
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
|
||||
);
|
||||
|
|
@ -1,128 +0,0 @@
|
|||
/*++
|
||||
|
||||
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 <ntddser.h>
|
||||
|
||||
#else
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Instead of #include <ntddser.h>, 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 <ntddser.h>
|
||||
|
||||
//
|
||||
// DEFINE_GUID(GUID_DEVINTERFACE_MODEM, 0x2c7089aa, 0x2e0e, 0x11d1, 0xb1, 0x14, 0x00, 0xc0, 0x4f, 0xc2, 0xaa, 0xe4);
|
||||
//
|
||||
#include <ntddmodm.h>
|
||||
Loading…
Reference in a new issue