using System; using Microsoft.Win32.SafeHandles; using System.Runtime.InteropServices; using DTS.Common.USBFramework; using DTS.Common.Utilities.Logging; namespace DTS.Common.WINUSBConnection { /// /// Routines for the WinUsb driver supported by Windows Vista and Windows XP. /// /// 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(); } /// /// Closes the device handle obtained with CreateFile and frees resources. /// /// 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 { } } /// /// Initiates a Control Read transfer. Data stage is device to host. /// /// /// The received data. /// /// /// True on success, False on failure. /// 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; } /// /// Initiates a Control Write transfer. Data stage is host to device. /// /// /// The data to send. /// /// /// True on success, False on failure. /// 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; } /// /// Requests a handle with CreateFile. /// /// /// Returned by SetupDiGetDeviceInterfaceDetail /// in an SP_DEVICE_INTERFACE_DETAIL_DATA structure. /// /// /// The handle. /// 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; } /// /// Initializes a device interface and obtains information about it. /// Calls these winusb API functions: /// WinUsb_Initialize /// WinUsb_QueryInterfaceSettings /// WinUsb_QueryPipe /// /// /// True on success, False on failure. /// 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; } /// /// Is the current operating system Windows XP or later? /// The WinUSB driver requires Windows XP or later. /// /// /// /// True if Windows XP or later, False if not. /// internal bool IsWindowsXpOrLater() { var myEnvironment = Environment.OSVersion; // Windows XP is version 5.1. var versionXP = new Version(5, 1); return myEnvironment.Version >= versionXP; } /// /// Gets a value that corresponds to a USBDeviceSpeeds. /// 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; } /// /// see winerror.h, these are error codes that we can expect or have received from /// read pipe /// 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; /// /// Attempts to read data from a bulk IN endpoint. /// /// Endpoint address. /// Number of bytes to read. /// Buffer for storing the bytes read. /// Number of bytes read. /// Success or failure status. /// 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; } } /// /// Attempts to read data from an interrupt IN endpoint. /// /// Endpoint address. /// Number of bytes to read. /// Buffer for storing the bytes read. /// Number of bytes read. /// Success or failure status. /// 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; } } /// /// Attempts to send data via a bulk OUT endpoint. /// /// /// Buffer containing the bytes to write. /// Number of bytes to write. /// /// /// True on success, False on failure. /// 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; } /// /// Attempts to send data via an interrupt OUT endpoint. /// /// /// Buffer containing the bytes to write. /// Number of bytes to write. /// /// /// True on success, False on failure. /// 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; } /// /// Sets pipe policy. /// Used when the value parameter is a Byte (all except PIPE_TRANSFER_TIMEOUT). /// /// /// Pipe to set a policy for. /// POLICY_TYPE member. /// Policy value. /// /// /// True on success, False on failure. /// /// 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; } /// /// Sets pipe policy. /// Used when the value parameter is a UInt32 (PIPE_TRANSFER_TIMEOUT only). /// /// /// Pipe to set a policy for. /// POLICY_TYPE member. /// Policy value. /// /// /// True on success, False on failure. /// /// 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; } /// /// Is the endpoint's direction IN (device to host)? /// /// /// The endpoint address. /// /// True if IN (device to host), False if OUT (host to device) /// private static bool UsbEndpointDirectionIn(int addr) { var usbEndpointDirectionInReturn = (addr & 0X80) == 0X80; return usbEndpointDirectionInReturn; } /// /// Is the endpoint's direction OUT (host to device)? /// /// /// The endpoint address. /// /// /// True if OUT (host to device, False if IN (device to host) /// private static bool UsbEndpointDirectionOut(int addr) { var usbEndpointDirectionOutReturn = (addr & 0X80) == 0; return usbEndpointDirectionOutReturn; } } }