284 lines
14 KiB
Markdown
284 lines
14 KiB
Markdown
---
|
||
source_files:
|
||
- Common/DTS.Common.IConnection/USBConnection/WINUSBConnection/WINUSBDeviceApi.cs
|
||
- Common/DTS.Common.IConnection/USBConnection/WINUSBConnection/CDCUSBConnection.cs
|
||
- Common/DTS.Common.IConnection/USBConnection/WINUSBConnection/WINUSBConnection.cs
|
||
generated_at: "2026-04-16T02:09:52.794052+00:00"
|
||
model: "Qwen/Qwen3-Coder-Next-FP8"
|
||
schema_version: 1
|
||
sha256: "2043cd8e3c0f04ef"
|
||
---
|
||
|
||
# Documentation: USB Connection Modules
|
||
|
||
## 1. Purpose
|
||
|
||
This module provides two distinct USB-based connection implementations (`CDCUSBConnection` and `WINUSBConnection`) that implement the `IConnection` interface for communicating with DTS hardware devices. `CDCUSBConnection` emulates a serial port over USB using the Windows `SerialPort` class (CDC–Communication Device Class), while `WINUSBConnection` uses the native Windows WinUSB API (`winusb.dll`) for direct USB communication. Both modules support asynchronous I/O operations (`BeginConnect`, `EndConnect`, `BeginSend`, `EndSend`, etc.), resource disposal, and registry-based device enumeration for the DTS vendor ID `0x1CB9` and specific product IDs.
|
||
|
||
## 2. Public Interface
|
||
|
||
### `CDCUSBConnection`
|
||
|
||
- **`public bool IsSoftDisconnected { get; }`**
|
||
Always returns `false`. Soft disconnect is not supported for CDCUSB.
|
||
|
||
- **`public void SoftConnect()`**
|
||
No-op. Soft connect is not supported.
|
||
|
||
- **`public void SoftDisconnect()`**
|
||
No-op. Soft disconnect is not supported.
|
||
|
||
- **`public double GetCurrentUploadRate()`**
|
||
Returns `0.0`. Upload rate is not tracked.
|
||
|
||
- **`public double GetCurrentDownloadRate()`**
|
||
Returns `0.0`. Download rate is not tracked.
|
||
|
||
- **`public string PortName { get; set; }`**
|
||
Gets or sets the COM port name (e.g., `"COM3"`) used for serial communication.
|
||
|
||
- **`public string ConnectString { get; }`**
|
||
Returns the `_devicePathname` passed to `Create`. Used as a device identifier.
|
||
|
||
- **`public bool Connected { get; }`**
|
||
Returns `_Connected`, indicating whether the underlying `SerialPort` is open.
|
||
|
||
- **`public event EventHandler OnDisconnected`**
|
||
Event raised on disconnection (currently unused in this implementation).
|
||
|
||
- **`public static IList<string> RegKeys { get; }`**
|
||
Lazily-initialized list of registry subkey names under the DTS CDCUSB device path (`USB\VID_1CB9&PID_001A`). Used to locate device-specific COM port names.
|
||
|
||
- **`public CDCUSBConnection()`**
|
||
Constructor initializing internal state (`_Connected = false`, `Disposed = false`, `Disposing = false`).
|
||
|
||
- **`public void Create(string connectString, string hostIPAddress)`**
|
||
Parses `connectString` to match a registry key in `RegKeys`, then reads the `"PortName"` value from the device’s registry `"Device Parameters"` subkey and assigns it to `PortName`. Sets `_devicePathname = connectString`.
|
||
|
||
- **`public IAsyncResult BeginConnect(AsyncCallback cb, object state)`**
|
||
Returns an `IAsyncResult` (`UsbRecAsyncResult`) and queues a callback via `ThreadPool.QueueUserWorkItem`. Does *not* perform the actual connection.
|
||
|
||
- **`public void EndConnect(IAsyncResult ar)`**
|
||
Opens the `SerialPort` using configured `_baudRate`, `_parity`, `_stopBits`, `DATA_BITS`, and `PortName`. Sets `_Connected = true` on success.
|
||
|
||
- **`public IAsyncResult BeginSend(byte[] buffer, int offset, int size, AsyncCallback cb, object state)`**
|
||
Returns `UsbRecAsyncResult` with buffer metadata. Actual write occurs in `EndSend`.
|
||
|
||
- **`public int EndSend(IAsyncResult ar)`**
|
||
Writes `buffer` (using `size` and `offset` from `UsbRecAsyncResult`) to `_comPort.Write(...)`. Returns `size`.
|
||
|
||
- **`public Task<int> SendAsync(...)`**
|
||
Wraps `BeginSend`/`EndSend` in a `Task<int>`.
|
||
|
||
- **`public IAsyncResult BeginReceive(byte[] buffer, int offset, int size, AsyncCallback cb, object state)`**
|
||
Returns `UsbRecAsyncResult` with buffer metadata.
|
||
|
||
- **`public int EndReceive(IAsyncResult ar)`**
|
||
Reads from `_comPort.Read(...)` in a loop until at least one byte is read (sleeps 1 ms between retries). Returns number of bytes read.
|
||
|
||
- **`public IAsyncResult BeginDisconnect(...)`**
|
||
Returns `UsbRecAsyncResult`. Actual disconnect occurs in `EndDisconnect`.
|
||
|
||
- **`public void EndDisconnect(IAsyncResult ar)`**
|
||
Closes and disposes `_comPort`, sets `_Connected = false`.
|
||
|
||
- **`public void Dispose()` / `protected virtual void Dispose(bool)`**
|
||
Implements `IDisposable`. Closes `_comPort` if open, sets `Disposed = true`.
|
||
|
||
- **`public void Bind(int port)` / `public void Listen(int backlog)` / `public IAsyncResult BeginAccept(...)` / `public IConnection EndAccept(...)`**
|
||
All throw `NotSupportedException`.
|
||
|
||
### `WINUSBConnection`
|
||
|
||
- **`public bool IsSoftDisconnected { get; }`**
|
||
Always returns `false`. Soft disconnect is not supported.
|
||
|
||
- **`public void SoftConnect()` / `public void SoftDisconnect()`**
|
||
No-op.
|
||
|
||
- **`public double GetCurrentUploadRate()` / `public double GetCurrentDownloadRate()`**
|
||
Return `0.0`.
|
||
|
||
- **`public bool Connected { get; private set; }`**
|
||
Indicates whether the device is connected via WinUSB.
|
||
|
||
- **`public string ConnectString { get; private set; }`**
|
||
Stores the device path passed to `Create`.
|
||
|
||
- **`public event EventHandler OnDisconnected`**
|
||
Event for disconnection notifications (currently unused).
|
||
|
||
- **`public void Create(string connectString, string hostIPAddress)`**
|
||
Stores `connectString` in `ConnectString`.
|
||
|
||
- **`public IAsyncResult BeginConnect(AsyncCallback cb, object state)`**
|
||
Validates `_sliceDev == null && !Connected`. Returns `WinUSBRecAsyncResult`, waits synchronously on `AsyncWaitHandle` before returning.
|
||
|
||
- **`public void EndConnect(IAsyncResult ar)`**
|
||
Instantiates `_sliceDev = new WinUsbDevice()`, calls `_sliceDev.GetDeviceHandle(ConnectString)` and `_sliceDev.InitializeDevice()`. Sets `Connected = true` on success. Logs and throws `NotConnectedException` on failure.
|
||
|
||
- **`public IAsyncResult BeginSend(byte[] buffer, int offset, int size, AsyncCallback cb, object state)`**
|
||
Validates connection and buffer parameters. Returns `WinUSBRecAsyncResult`.
|
||
|
||
- **`public int EndSend(IAsyncResult ar)`**
|
||
Copies buffer data, then calls `_sliceDev.SendViaInterruptTransfer(...)` or `_sliceDev.SendViaBulkTransfer(...)` depending on `MyDevInfo.UseHybridBulkIntMode`. Throws on failure.
|
||
|
||
- **`public Task<int> SendAsync(...)`**
|
||
Wraps `BeginSend`/`EndSend`.
|
||
|
||
- **`public IAsyncResult BeginReceive(byte[] buffer, int offset, int size, AsyncCallback cb, object state)`**
|
||
Validates connection and buffer parameters. Returns `WinUSBRecAsyncResult`.
|
||
|
||
- **`public int EndReceive(IAsyncResult ar)`**
|
||
Calls `_sliceDev.ReadViaBulkTransfer(...)` in a loop until `bytesRead > 0` or failure. Copies data into `rar.Buffer`. Returns bytes read.
|
||
|
||
- **`public IAsyncResult BeginDisconnect(...)`**
|
||
Validates `_sliceDev != null`. Sets `Connected = false`, returns `WinUSBRecAsyncResult`.
|
||
|
||
- **`public void EndDisconnect(IAsyncResult ar)`**
|
||
Calls `DisposeSliceDev()` to release WinUSB handle.
|
||
|
||
- **`public void Dispose()` / `protected virtual void Dispose(bool)`**
|
||
Implements `IDisposable`. Disposes `_wusbDeviceManagement` and calls `DisposeSliceDev()`. Uses `Mutex _usbConnectionMutex` for thread safety.
|
||
|
||
- **`public void Bind(int port)` / `public void Listen(int backlog)` / `public IAsyncResult BeginAccept(...)` / `public IConnection EndAccept(...)`**
|
||
All throw `NotSupportedException`.
|
||
|
||
### `WinUsbDevice` (internal)
|
||
|
||
- **`internal const uint DEVICE_SPEED = 1`**
|
||
Used with `WinUsb_QueryDeviceInformation`.
|
||
|
||
- **`internal const byte USB_ENDPOINT_DIRECTION_MASK = 0x80`**
|
||
Mask to determine endpoint direction (IN/OUT).
|
||
|
||
- **`internal enum PolicyType`**
|
||
Defines WinUSB pipe policies: `ShortPacketTerminate`, `AutoClearStall`, `PipeTransferTimeout`, etc.
|
||
|
||
- **`internal enum USBDPipeTypes`**
|
||
Pipe types: `UsbdPipeTypeControl`, `UsbdPipeTypeIsochronous`, `UsbdPipeTypeBulk`, `UsbdPipeTypeInterrupt`.
|
||
|
||
- **`internal enum USBDeviceSpeeds`**
|
||
Device speeds: `UsbLowSpeed`, `UsbFullSpeed`, `UsbHighSpeed`.
|
||
|
||
- **`internal struct USBConfigurationDescriptor`**
|
||
P/Invoke struct for USB configuration descriptor.
|
||
|
||
- **`internal struct USBInterfaceDescriptor`**
|
||
P/Invoke struct for USB interface descriptor.
|
||
|
||
- **`internal struct WinUSBPipeInformation`**
|
||
P/Invoke struct for pipe info: `PipeTypes`, `PipeId`, `MaximumPacketSize`, `Interval`.
|
||
|
||
- **`internal struct WinUSBSetupPacket`**
|
||
P/Invoke struct for control transfer setup packet.
|
||
|
||
- **`internal static extern bool WinUsb_ControlTransfer(...)`**
|
||
Sends control transfers.
|
||
|
||
- **`internal static extern bool WinUsb_Initialize(...)`**
|
||
Initializes WinUSB interface handle.
|
||
|
||
- **`internal static extern bool WinUsb_Free(...)`**
|
||
Frees WinUSB interface handle.
|
||
|
||
- **`internal static extern bool WinUsb_QueryDeviceInformation(...)`**
|
||
Retrieves device info (e.g., speed) using `DEVICE_SPEED`.
|
||
|
||
- **`internal static extern bool WinUsb_QueryInterfaceSettings(...)`**
|
||
Queries interface settings.
|
||
|
||
- **`internal static extern bool WinUsb_QueryPipe(...)`**
|
||
Queries pipe info.
|
||
|
||
- **`internal static extern bool WinUsb_ReadPipe(...)`**
|
||
Reads from a pipe.
|
||
|
||
- **`internal static extern bool WinUsb_WritePipe(...)`**
|
||
Writes to a pipe.
|
||
|
||
- **`internal static extern bool WinUsb_SetPipePolicy(...)`**
|
||
Sets pipe policy (byte value).
|
||
**Alias**: `WinUsb_SetPipePolicy1` for `PIPE_TRANSFER_TIMEOUT` (uses `uint` value).
|
||
|
||
## 3. Invariants
|
||
|
||
- **`Connected` state**
|
||
- `CDCUSBConnection`: `_Connected` is `true` only when `_comPort.IsOpen == true`.
|
||
- `WINUSBConnection`: `Connected` is `true` only after `_sliceDev.GetDeviceHandle(...)` and `_sliceDev.InitializeDevice()` both succeed.
|
||
|
||
- **`ConnectString` usage**
|
||
- `CDCUSBConnection`: Used to match registry keys for COM port lookup.
|
||
- `WINUSBConnection`: Passed to `_sliceDev.GetDeviceHandle(...)` to locate device.
|
||
|
||
- **Thread safety**
|
||
- `WINUSBConnection` uses `_usbConnectionMutex` around `WinUsbDevice` disposal.
|
||
- `CDCUSBConnection.RegKeys` uses `KEY_LOCK` for lazy initialization.
|
||
|
||
- **Asynchronous pattern**
|
||
- `Begin*` methods queue work to `ThreadPool` via `NetCallbackFix`, which invokes the callback.
|
||
- `End*` methods perform actual I/O and signal `AsyncWaitHandle.Set()`.
|
||
|
||
- **No soft disconnect**
|
||
Both `CDCUSBConnection` and `WINUSBConnection` have no-op `SoftConnect`/`SoftDisconnect`.
|
||
|
||
- **No socket operations**
|
||
`Bind`, `Listen`, `BeginAccept`, `EndAccept` throw `NotSupportedException` in both classes.
|
||
|
||
## 4. Dependencies
|
||
|
||
### Imports / External Dependencies
|
||
- **Windows API**: `winusb.dll` (for `WINUSBConnection` via P/Invoke in `WinUsbDevice`).
|
||
- **.NET Framework**:
|
||
- `System.IO.Ports.SerialPort` (for `CDCUSBConnection`).
|
||
- `System.Threading`, `System.Threading.Tasks`, `System.Runtime.InteropServices`.
|
||
- `Microsoft.Win32.Registry` (for registry enumeration).
|
||
- **Internal DTS modules** (from namespace imports):
|
||
- `DTS.Common.DASResource` (string resources, e.g., `DASResource.Strings.*`).
|
||
- `DTS.Common.Interface.Connection` (`IConnection` interface).
|
||
- `DTS.Common.Utilities.Logging` (`APILogger`).
|
||
- `DTS.Common.USBFramework` (likely contains `DeviceManagement`).
|
||
- `DTS.Common.Classes.Connection` (likely contains `NotConnectedException`).
|
||
|
||
### Dependencies on Other Modules
|
||
- `WINUSBConnection` depends on `DeviceManagement` and `WinUsbDevice` (defined in same namespace).
|
||
- `CDCUSBConnection` depends on `DASResource.Strings` for exception messages.
|
||
|
||
### Dependencies *by* This Module
|
||
- `IConnection` interface (consumed by higher layers).
|
||
- `DeviceManagement` (used by `WINUSBConnection` for device lifecycle management).
|
||
|
||
## 5. Gotchas
|
||
|
||
- **`Create` recursion**
|
||
Both `CDCUSBConnection.Create(string)` and `WINUSBConnection.Create(string)` contain commented-out recursive calls (`//Create(connectString); - this is recursive!`). This suggests a historical bug or incomplete refactoring.
|
||
|
||
- **`BeginConnect` blocking in `WINUSBConnection`**
|
||
`WINUSBConnection.BeginConnect` calls `rar.AsyncWaitHandle.WaitOne()` *before returning*, making it effectively synchronous despite being an "Async" pattern. This violates the expected non-blocking behavior of `Begin*` methods.
|
||
|
||
- **`EndConnect` in `WINUSBConnection` throws if already connected**
|
||
`EndConnect` checks `if (_sliceDev != null || Connected)` and throws `NotConnectedException`, but `BeginConnect` already sets `Connected = false` *before* the async work. This is inconsistent and may cause confusion.
|
||
|
||
- **`EndReceive` busy-waits**
|
||
`CDCUSBConnection.EndReceive` and `WINUSBConnection.EndReceive` both use tight loops with `Thread.Sleep(1)` when no data is available. This can cause high CPU usage.
|
||
|
||
- **`WinUSB_SetPipePolicy` vs `WinUsb_SetPipePolicy1`**
|
||
Two separate P/Invoke declarations exist for `WinUsb_SetPipePolicy`, with `WinUsb_SetPipePolicy1` used *only* for `PIPE_TRANSFER_TIMEOUT` (which requires a `uint` instead of `byte`). This is error-prone and not enforced by the API.
|
||
|
||
- **`RegKeys` is static and lazily initialized**
|
||
`CDCUSBConnection.RegKeys` is a static property. If the registry changes after first access, the list will be stale until app restart.
|
||
|
||
- **No error propagation in `NetCallbackFix`**
|
||
Exceptions in `NetCallbackFix` are caught and logged, but the callback is still invoked. This may leave callers expecting an exception to be thrown in `End*`.
|
||
|
||
- **`IsSoftDisconnected` always false**
|
||
Both classes report `IsSoftDisconnected = false`, but the interface contract may expect it to reflect a real state. This could mislead callers.
|
||
|
||
- **`GetConnectionData()` returns `""`**
|
||
Both classes return empty strings. This may indicate incomplete implementation.
|
||
|
||
- **`Flags` property unused**
|
||
`System.Net.Sockets.SocketFlags Flags { get; set; }` is exposed but never used in either class.
|
||
|
||
- **`USB_ENDPOINT_DIRECTION_MASK` is internal**
|
||
While defined, it is not used in the provided source. May be used elsewhere in the codebase. |