Files
DP44/enriched-qwen3-coder-next/Common/DTS.Common.IConnection/USBConnection/WINUSBConnection.md
2026-04-17 14:55:32 -04:00

284 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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 (CDCCommunication 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 devices 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.