Files
DP44/Common/DTS.Common.IConnection/.svn/pristine/67/67ba07e15fc84756549ddb8f2468ec93fbc5e4f4.svn-base

828 lines
31 KiB
Plaintext
Raw Permalink Normal View History

2026-04-17 14:55:32 -04:00
using System;
using Microsoft.Win32.SafeHandles;
using System.Runtime.InteropServices;
using DTS.Common.USBFramework;
using DTS.Common.Utilities.Logging;
namespace DTS.Common.WINUSBConnection
{
/// <summary>
/// Routines for the WinUsb driver supported by Windows Vista and Windows XP.
/// </summary>
///
internal sealed partial class WinUsbDevice : IDisposable
{
private volatile bool _bDisposed;
~WinUsbDevice()
{
if (!_bDisposed)
{
try
{
APILogger.Log("WinUSBDevice not disposed before finalizer. Disposing now");
}
catch { }
Dispose();
}
}
internal struct DevInfo
{
internal SafeFileHandle DeviceHandle;
internal IntPtr WinUSBHandle;
internal Byte BulkInPipe;
internal Byte BulkOutPipe;
internal Byte InterruptInPipe;
internal Byte InterruptOutPipe;
internal UInt32 DeviceSpeed;
internal bool UseHybridBulkIntMode;
}
internal DevInfo MyDevInfo;
public void Dispose()
{
if (_bDisposed) return;
_bDisposed = true;
CloseDeviceHandle();
}
/// <summary>
/// Closes the device handle obtained with CreateFile and frees resources.
/// </summary>
///
internal void CloseDeviceHandle()
{
try
{
if (MyDevInfo.WinUSBHandle != IntPtr.Zero)
{
WinUsb_Free(MyDevInfo.WinUSBHandle);
MyDevInfo.WinUSBHandle = IntPtr.Zero;
}
}
catch (AccessViolationException)
{
}
try
{
if (MyDevInfo.DeviceHandle == null || MyDevInfo.DeviceHandle.IsInvalid) return;
if (MyDevInfo.DeviceHandle.IsInvalid) return;
MyDevInfo.DeviceHandle.Dispose();
MyDevInfo.DeviceHandle = null;
}
catch
{
}
}
/// <summary>
/// Initiates a Control Read transfer. Data stage is device to host.
/// </summary>
///
/// <param name="dataStage"> The received data. </param>
///
/// <returns>
/// True on success, False on failure.
/// </returns>
internal bool Do_Control_Read_Transfer(ref byte[] dataStage)
{
// Vendor-specific request to an interface with device-to-host Data stage.
WinUSBSetupPacket setupPacket;
setupPacket.RequestType = 0XC1;
// The request number that identifies the specific request.
setupPacket.Request = 2;
// Command-specific value to send to the device.
setupPacket.Index = 0;
// Number of bytes in the request's Data stage.
setupPacket.Length = Convert.ToUInt16(dataStage.Length);
// Command-specific value to send to the device.
setupPacket.Value = 0;
// ***
// winusb function
// summary
// Initiates a control transfer.
// paramaters
// Device handle returned by WinUsb_Initialize.
// WinUSBSetupPacket structure
// Buffer to hold the returned Data-stage data.
// Number of data bytes to read in the Data stage.
// Number of bytes read in the Data stage.
// Null pointer for non-overlapped.
// returns
// True on success.
// ***
uint bytesReturned = 0;
var success = WinUsb_ControlTransfer(MyDevInfo.WinUSBHandle,
setupPacket,
dataStage,
Convert.ToUInt16(dataStage.Length),
ref bytesReturned,
IntPtr.Zero);
return success;
}
/// <summary>
/// Initiates a Control Write transfer. Data stage is host to device.
/// </summary>
///
/// <param name="dataStage"> The data to send. </param>
///
/// <returns>
/// True on success, False on failure.
/// </returns>
internal bool Do_Control_Write_Transfer(byte[] dataStage)
{
// Vendor-specific request to an interface with host-to-device Data stage.
WinUSBSetupPacket setupPacket;
setupPacket.RequestType = 0X41;
// The request number that identifies the specific request.
setupPacket.Request = 1;
// Command-specific value to send to the device.
var index = Convert.ToUInt16(0);
setupPacket.Index = index;
// Number of bytes in the request's Data stage.
setupPacket.Length = Convert.ToUInt16(dataStage.Length);
// Command-specific value to send to the device.
var value = Convert.ToUInt16(0);
setupPacket.Value = value;
// ***
// winusb function
// summary
// Initiates a control transfer.
// parameters
// Device handle returned by WinUsb_Initialize.
// WinUSBSetupPacket structure
// Buffer containing the Data-stage data.
// Number of data bytes to send in the Data stage.
// Number of bytes sent in the Data stage.
// Null pointer for non-overlapped.
// Returns
// True on success.
// ***
uint bytesReturned = 0;
var success = WinUsb_ControlTransfer(MyDevInfo.WinUSBHandle,
setupPacket,
dataStage,
Convert.ToUInt16(dataStage.Length),
ref bytesReturned,
IntPtr.Zero);
return success;
}
/// <summary>
/// Requests a handle with CreateFile.
/// </summary>
///
/// <param name="devicePathName"> Returned by SetupDiGetDeviceInterfaceDetail
/// in an SP_DEVICE_INTERFACE_DETAIL_DATA structure. </param>
///
/// <returns>
/// The handle.
/// </returns>
internal bool GetDeviceHandle(string devicePathName)
{
var security = new FileIO.SECURITY_ATTRIBUTES
{
lpSecurityDescriptor = IntPtr.Zero,
bInheritHandle = Convert.ToInt32(true)
};
security.nLength = Marshal.SizeOf(security);
System.Diagnostics.Trace.Assert(null == MyDevInfo.DeviceHandle || MyDevInfo.DeviceHandle.IsClosed);
// ***
// API function
// summary
// Retrieves a handle to a device.
// parameters
// Device path name returned by SetupDiGetDeviceInterfaceDetail
// Type of access requested (read/write).
// FILE_SHARE attributes to allow other processes to access the device while this handle is open.
// Security structure. Using Null for this may cause problems under Windows XP.
// Creation disposition value. Use OPEN_EXISTING for devices.
// Flags and attributes for files. The winsub driver requires FILE_FLAG_OVERLAPPED.
// Handle to a template file. Not used.
// Returns
// A handle or INVALID_HANDLE_VALUE.
// ***
MyDevInfo.DeviceHandle = FileIO.CreateFile(devicePathName,
FileIO.GENERIC_WRITE | FileIO.GENERIC_READ,
FileIO.FILE_SHARE_READ | FileIO.FILE_SHARE_WRITE,
ref security,
FileIO.OPEN_EXISTING,
FileIO.FILE_ATTRIBUTE_NORMAL | FileIO.FILE_FLAG_OVERLAPPED,
0);
if (!MyDevInfo.DeviceHandle.IsInvalid)
{
return true;
}
FileIODeclarations.GetLastError();
try
{
var w32 = new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
APILogger.Log("GetDeviceHandle(", devicePathName, ") - error - ", w32.Message);
}
catch { }
return false;
}
/// <summary>
/// Initializes a device interface and obtains information about it.
/// Calls these winusb API functions:
/// WinUsb_Initialize
/// WinUsb_QueryInterfaceSettings
/// WinUsb_QueryPipe
/// </summary>
/// <returns>
/// True on success, False on failure.
/// </returns>
internal bool InitializeDevice()
{
USBInterfaceDescriptor ifaceDescriptor;
ifaceDescriptor.bLength = 0;
ifaceDescriptor.bDescriptorType = 0;
ifaceDescriptor.bInterfaceNumber = 0;
ifaceDescriptor.bAlternateSetting = 0;
ifaceDescriptor.bNumEndpoints = 0;
ifaceDescriptor.bInterfaceClass = 0;
ifaceDescriptor.bInterfaceSubClass = 0;
ifaceDescriptor.bInterfaceProtocol = 0;
ifaceDescriptor.iInterface = 0;
WinUSBPipeInformation pipeInfo;
pipeInfo.PipeTypes = 0;
pipeInfo.PipeId = 0;
pipeInfo.MaximumPacketSize = 0;
pipeInfo.Interval = 0;
MyDevInfo.UseHybridBulkIntMode = false;
// ***
// winusb function
// summary
// get a handle for communications with a winusb device '
// parameters
// Handle returned by CreateFile.
// Device handle to be returned.
// returns
// True on success.
// ***
var success = WinUsb_Initialize(MyDevInfo.DeviceHandle, ref MyDevInfo.WinUSBHandle);
if (!success) return false;
// ***
// winusb function
// summary
// Get a structure with information about the device interface.
// parameters
// handle returned by WinUsb_Initialize
// alternate interface setting number
// USBInterfaceDescriptor structure to be returned.
// returns
// True on success.
success = WinUsb_QueryInterfaceSettings(MyDevInfo.WinUSBHandle,
0,
ref ifaceDescriptor);
if (success)
{
// Get the transfer type, endpoint number, and direction for the interface's
// bulk and interrupt endpoints. Set pipe policies.
// ***
// winusb function
// summary
// returns information about a USB pipe (endpoint address)
// parameters
// Handle returned by WinUsb_Initialize
// Alternate interface setting number
// Number of an endpoint address associated with the interface.
// (The values count up from zero and are NOT the same as the endpoint address
// in the endpoint descriptor.)
// WinUSBPipeInformation structure to be returned
// returns
// True on success
// ***
var pipeTimeout = Convert.ToUInt32(0);
for (var i = 0; i <= ifaceDescriptor.bNumEndpoints - 1; i++)
{
WinUsb_QueryPipe(MyDevInfo.WinUSBHandle,
0,
Convert.ToByte(i),
ref pipeInfo);
if (pipeInfo.PipeTypes == USBDPipeTypes.UsbdPipeTypeBulk && UsbEndpointDirectionIn(pipeInfo.PipeId))
{
MyDevInfo.BulkInPipe = pipeInfo.PipeId;
SetPipePolicy
(MyDevInfo.BulkInPipe,
Convert.ToUInt32(PolicyType.IgnoreShortPackets),
Convert.ToByte(false));
SetPipePolicy
(MyDevInfo.BulkInPipe,
Convert.ToUInt32(PolicyType.PipeTransferTimeout),
pipeTimeout);
}
else if (pipeInfo.PipeTypes == USBDPipeTypes.UsbdPipeTypeBulk && UsbEndpointDirectionOut(pipeInfo.PipeId))
{
MyDevInfo.BulkOutPipe = pipeInfo.PipeId;
SetPipePolicy
(MyDevInfo.BulkOutPipe,
Convert.ToUInt32(PolicyType.IgnoreShortPackets),
Convert.ToByte(false));
SetPipePolicy
(MyDevInfo.BulkOutPipe,
Convert.ToUInt32(PolicyType.PipeTransferTimeout),
pipeTimeout);
}
else if (pipeInfo.PipeTypes == USBDPipeTypes.UsbdPipeTypeInterrupt && UsbEndpointDirectionIn(pipeInfo.PipeId))
{
MyDevInfo.InterruptInPipe = pipeInfo.PipeId;
SetPipePolicy
(MyDevInfo.InterruptInPipe,
Convert.ToUInt32(PolicyType.IgnoreShortPackets),
Convert.ToByte(false));
SetPipePolicy
(MyDevInfo.InterruptInPipe,
Convert.ToUInt32(PolicyType.PipeTransferTimeout),
pipeTimeout);
}
else if (pipeInfo.PipeTypes == USBDPipeTypes.UsbdPipeTypeInterrupt && UsbEndpointDirectionOut(pipeInfo.PipeId))
{
MyDevInfo.InterruptOutPipe = pipeInfo.PipeId;
SetPipePolicy
(MyDevInfo.InterruptOutPipe,
Convert.ToUInt32(PolicyType.IgnoreShortPackets),
Convert.ToByte(false));
SetPipePolicy
(MyDevInfo.InterruptOutPipe,
Convert.ToUInt32(PolicyType.PipeTransferTimeout),
pipeTimeout);
MyDevInfo.UseHybridBulkIntMode = true;
}
}
}
return success;
}
/// <summary>
/// Is the current operating system Windows XP or later?
/// The WinUSB driver requires Windows XP or later.
/// </summary>
///
/// <returns>
/// True if Windows XP or later, False if not.
/// </returns>
internal bool IsWindowsXpOrLater()
{
var myEnvironment = Environment.OSVersion;
// Windows XP is version 5.1.
var versionXP = new Version(5, 1);
return myEnvironment.Version >= versionXP;
}
/// <summary>
/// Gets a value that corresponds to a USBDeviceSpeeds.
/// </summary>
internal bool QueryDeviceSpeed()
{
// ***
// winusb function
// summary
// Get the device speed.
// (Normally not required but can be nice to know.)
// parameters
// Handle returned by WinUsb_Initialize
// Requested information type.
// Number of bytes to read.
// Information to be returned.
// returns
// True on success.
// ***
uint length = 1;
var speed = new byte[1];
var success = WinUsb_QueryDeviceInformation(MyDevInfo.WinUSBHandle,
DEVICE_SPEED,
ref length,
ref speed[0]);
if (success)
{
MyDevInfo.DeviceSpeed = Convert.ToUInt32(speed[0]);
}
return success;
}
/// <summary>
/// see winerror.h, these are error codes that we can expect or have received from
/// read pipe
/// </summary>
private const long ERROR_GEN_FAILURE = 31L;
private const long ERROR_LOCK_VIOLATION = 33L;
private const long ERROR_INVALID_HANDLE = 6L;
private const long ERROR_OPERATION_ABORTED = 995L;
private const long ERROR_IO_INCOMPLETE = 996L;
private const long ERROR_IO_PENDING = 997L;
private const long ERROR_NOT_ENOUGH_MEMORY = 8L;
private const long ERROR_SEM_TIMEOUT = 121L;
/// <summary>
/// Attempts to read data from a bulk IN endpoint.
/// </summary>
/// <param name="pipeId"> Endpoint address. </param>
/// <param name="bytesToRead"> Number of bytes to read. </param>
/// <param name="buffer"> Buffer for storing the bytes read. </param>
/// <param name="bytesRead"> Number of bytes read. </param>
/// <param name="success"> Success or failure status. </param>
///
internal void ReadViaBulkTransfer(byte pipeId,
uint bytesToRead,
ref byte[] buffer,
ref uint bytesRead,
ref bool success)
{
try
{
// ***
// winusb function
// summary
// Attempts to read data from a device interface.
// parameters
// Device handle returned by WinUsb_Initialize.
// Endpoint address.
// Buffer to store the data.
// Maximum number of bytes to return.
// Number of bytes read.
// Null pointer for non-overlapped.
// Returns
// True on success.
// ***
success = WinUsb_ReadPipe
(MyDevInfo.WinUSBHandle,
pipeId,
buffer,
bytesToRead,
ref bytesRead,
IntPtr.Zero);
if (success) return;
long lastError = Marshal.GetLastWin32Error();
try
{
var w32 = new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
switch (w32.NativeErrorCode)
{
case (int)ERROR_IO_PENDING:
case (int)ERROR_IO_INCOMPLETE:
case (int)ERROR_OPERATION_ABORTED:
break;
default:
APILogger.Log(string.Format("ReadViaBulkTransfer failed Message:{0} Error: {1}", w32.Message, w32.NativeErrorCode));
break;
}
}
catch { }
switch (lastError)
{
case ERROR_GEN_FAILURE:
//APILogger.LogString("WinUSBDevice::WinUsb_ReadPipe General failure - " + ERROR_GEN_FAILURE.ToString());
break;
case ERROR_LOCK_VIOLATION:
//APILogger.LogString("WinUSBDevice::WinUsb_ReadPipe Lock violation - " + ERROR_LOCK_VIOLATION.ToString());
break;
case ERROR_INVALID_HANDLE://do we clean up if the handle is invalid?
//APILogger.LogString("WinUSBDevice::WinUsb_ReadPipe Invalid Handle - " + ERROR_INVALID_HANDLE.ToString());
break;
case ERROR_IO_PENDING://this one should be okay
//APILogger.LogString("WinUSBDevice::WinUsb_ReadPipe I/O Pending - " + ERROR_IO_PENDING.ToString());
break;
case ERROR_NOT_ENOUGH_MEMORY://this we'd probably never see
//APILogger.LogString("WinUSBDevice::WinUsb_ReadPipe Not Enough Memory - " + ERROR_NOT_ENOUGH_MEMORY.ToString());
break;
case ERROR_SEM_TIMEOUT:
//APILogger.LogString("WinUSBDevice::WinUsb_ReadPipe Semaphore timeout - " + ERROR_SEM_TIMEOUT.ToString());
break;
}
}
catch (Exception ex)
{
try
{
var w32 = new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
APILogger.Log("ReadViaBulkTranser - error ", ex.Message, " - ", w32.Message);
}
catch { }
throw;
}
}
/// <summary>
/// Attempts to read data from an interrupt IN endpoint.
/// </summary>
/// <param name="pipeId"> Endpoint address. </param>
/// <param name="bytesToRead"> Number of bytes to read. </param>
/// <param name="buffer"> Buffer for storing the bytes read. </param>
/// <param name="bytesRead"> Number of bytes read. </param>
/// <param name="success"> Success or failure status. </param>
///
internal void ReadViaInterruptTransfer(byte pipeId,
uint bytesToRead,
ref byte[] buffer,
ref uint bytesRead,
ref bool success)
{
try
{
// ***
// winusb function
// summary
// Attempts to read data from a device interface.
// parameters
// Device handle returned by WinUsb_Initialize.
// Endpoint address.
// Buffer to store the data.
// Maximum number of bytes to return.
// Number of bytes read.
// Null pointer for non-overlapped.
// Returns
// True on success.
// ***
success = WinUsb_ReadPipe(MyDevInfo.WinUSBHandle,
pipeId,
buffer,
bytesToRead,
ref bytesRead,
IntPtr.Zero);
if (success) return;
try
{
var w32 = new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
APILogger.Log("ReadViaInterruptTransfer failed, ", w32.Message);
}
catch { }
}
catch (Exception ex)
{
var w32 = new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
APILogger.Log("ReadViaInterruptTransfer failed, ", ex.Message, " - ", w32.Message);
throw;
}
}
/// <summary>
/// Attempts to send data via a bulk OUT endpoint.
/// </summary>
///
/// <param name="buffer"> Buffer containing the bytes to write. </param>
/// <param name="bytesToWrite"> Number of bytes to write. </param>
///
/// <returns>
/// True on success, False on failure.
/// </returns>
internal bool SendViaBulkTransfer(ref byte[] buffer, uint bytesToWrite)
{
uint bytesWritten = 0;
// ***
// winusb function
// summary
// Attempts to write data to a device interface.
// parameters
// Device handle returned by WinUsb_Initialize.
// Endpoint address.
// Buffer with data to write.
// Number of bytes to write.
// Number of bytes written.
// IntPtr.Zero for non-overlapped I/O.
// Returns
// True on success.
// ***
System.Threading.Thread.Sleep(5);
var success = WinUsb_WritePipe
(MyDevInfo.WinUSBHandle,
MyDevInfo.BulkOutPipe,
buffer,
bytesToWrite,
ref bytesWritten,
IntPtr.Zero);
if (success) return true;
try
{
var w32 = new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
APILogger.Log("WritePipe failed, buffer[", BitConverter.ToString(buffer, 0, Convert.ToInt32(bytesToWrite)), "], bytes: ", bytesToWrite, ", ", w32.Message);
}
catch { }
return false;
}
/// <summary>
/// Attempts to send data via an interrupt OUT endpoint.
/// </summary>
///
/// <param name="buffer"> Buffer containing the bytes to write. </param>
/// <param name="bytesToWrite"> Number of bytes to write. </param>
///
/// <returns>
/// True on success, False on failure.
/// </returns>
internal bool SendViaInterruptTransfer(ref byte[] buffer, uint bytesToWrite)
{
uint bytesWritten = 0;
// ***
// winusb function
// summary
// Attempts to write data to a device interface.
// parameters
// Device handle returned by WinUsb_Initialize.
// Endpoint address.
// Buffer with data to write.
// Number of bytes to write.
// Number of bytes written.
// IntPtr.Zero for non-overlapped I/O.
// Returns
// True on success.
// ***
var success = WinUsb_WritePipe
(MyDevInfo.WinUSBHandle,
MyDevInfo.InterruptOutPipe,
buffer,
bytesToWrite,
ref bytesWritten,
IntPtr.Zero);
if (success) return true;
try
{
var w32 = new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
APILogger.Log("SendViaInterruptTransfer failed - bytes[", BitConverter.ToString(buffer, 0, Convert.ToInt32(bytesToWrite)), "] - ", w32.Message);
}
catch { }
return false;
}
/// <summary>
/// Sets pipe policy.
/// Used when the value parameter is a Byte (all except PIPE_TRANSFER_TIMEOUT).
/// </summary>
///
/// <param name="pipeId"> Pipe to set a policy for. </param>
/// <param name="policyType"> POLICY_TYPE member. </param>
/// <param name="value"> Policy value. </param>
///
/// <returns>
/// True on success, False on failure.
/// </returns>
///
private bool SetPipePolicy(byte pipeId, uint policyType, byte value)
{
// ***
// winusb function
// summary
// sets a pipe policy
// parameters
// handle returned by WinUsb_Initialize
// identifies the pipe
// POLICY_TYPE member.
// length of value in bytes
// value to set for the policy.
// returns
// True on success
// ***
var success = WinUsb_SetPipePolicy
(MyDevInfo.WinUSBHandle,
pipeId,
policyType,
Convert.ToUInt32(1),
ref value);
return success;
}
/// <summary>
/// Sets pipe policy.
/// Used when the value parameter is a UInt32 (PIPE_TRANSFER_TIMEOUT only).
/// </summary>
///
/// <param name="pipeId"> Pipe to set a policy for. </param>
/// <param name="policyType"> POLICY_TYPE member. </param>
/// <param name="value"> Policy value. </param>
///
/// <returns>
/// True on success, False on failure.
/// </returns>
///
private bool SetPipePolicy(byte pipeId, uint policyType, uint value)
{
// ***
// winusb function
// summary
// sets a pipe policy
// parameters
// handle returned by WinUsb_Initialize
// identifies the pipe
// POLICY_TYPE member.
// length of value in bytes
// value to set for the policy.
// returns
// True on success
// ***
var success = WinUsb_SetPipePolicy1
(MyDevInfo.WinUSBHandle,
pipeId,
policyType,
Convert.ToUInt32(4),
ref value);
return success;
}
/// <summary>
/// Is the endpoint's direction IN (device to host)?
/// </summary>
///
/// <param name="addr"> The endpoint address. </param>
/// <returns>
/// True if IN (device to host), False if OUT (host to device)
/// </returns>
private static bool UsbEndpointDirectionIn(int addr)
{
var usbEndpointDirectionInReturn = (addr & 0X80) == 0X80;
return usbEndpointDirectionInReturn;
}
/// <summary>
/// Is the endpoint's direction OUT (host to device)?
/// </summary>
///
/// <param name="addr"> The endpoint address. </param>
///
/// <returns>
/// True if OUT (host to device, False if IN (device to host)
/// </returns>
private static bool UsbEndpointDirectionOut(int addr)
{
var usbEndpointDirectionOutReturn = (addr & 0X80) == 0;
return usbEndpointDirectionOutReturn;
}
}
}