Files
DP44/enriched-qwen3-coder-next/Common/DTS.Common.IConnection/USBConnection/WINUSBConnection.md

284 lines
14 KiB
Markdown
Raw Normal View History

2026-04-17 14:55:32 -04:00
---
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.