Files
2026-04-17 14:55:32 -04:00

14 KiB
Raw Permalink Blame History

source_files, generated_at, model, schema_version, sha256
source_files generated_at model schema_version sha256
Common/DTS.Common.IConnection/USBConnection/WINUSBConnection/WINUSBDeviceApi.cs
Common/DTS.Common.IConnection/USBConnection/WINUSBConnection/CDCUSBConnection.cs
Common/DTS.Common.IConnection/USBConnection/WINUSBConnection/WINUSBConnection.cs
2026-04-16T02:09:52.794052+00:00 Qwen/Qwen3-Coder-Next-FP8 1 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.