From baebd293c5e8cc41ebcd11464868e73c0a8044db Mon Sep 17 00:00:00 2001 From: Alexey 'Cluster' Avdyukhin Date: Sat, 7 Jan 2017 19:18:29 +0300 Subject: First commit --- FelLib/AWFELMessage.cs | 55 ++ FelLib/AWFELStandardRequest.cs | 47 ++ FelLib/AWFELStatusResponse.cs | 39 ++ FelLib/AWFELVerifyDeviceResponse.cs | 67 +++ FelLib/AWUSBRequest.cs | 60 +++ FelLib/AWUSBResponse.cs | 50 ++ FelLib/Fel.cs | 200 ++++++++ FelLib/FelException.cs | 14 + FelLib/FelParseException.cs | 11 + FelLib/WinUSBNet/API/APIException.cs | 45 ++ FelLib/WinUSBNet/API/DeviceDetails.cs | 23 + FelLib/WinUSBNet/API/DeviceManagement.cs | 311 ++++++++++++ FelLib/WinUSBNet/API/DeviceManagementAPI.cs | 159 ++++++ FelLib/WinUSBNet/API/FileAPI.cs | 39 ++ FelLib/WinUSBNet/API/WinUSBDevice.cs | 477 ++++++++++++++++++ FelLib/WinUSBNet/API/WinUSBDeviceAPI.cs | 202 ++++++++ FelLib/WinUSBNet/DeviceNotifyHook.cs | 117 +++++ FelLib/WinUSBNet/USB.cs | 80 +++ FelLib/WinUSBNet/USBAsyncResult.cs | 136 +++++ FelLib/WinUSBNet/USBDevice.cs | 756 ++++++++++++++++++++++++++++ FelLib/WinUSBNet/USBDeviceDescriptor.cs | 135 +++++ FelLib/WinUSBNet/USBDeviceInfo.cs | 85 ++++ FelLib/WinUSBNet/USBException.cs | 40 ++ FelLib/WinUSBNet/USBInterface.cs | 152 ++++++ FelLib/WinUSBNet/USBInterfaceCollection.cs | 160 ++++++ FelLib/WinUSBNet/USBNotifier.cs | 190 +++++++ FelLib/WinUSBNet/USBPipe.cs | 387 ++++++++++++++ FelLib/WinUSBNet/USBPipeCollection.cs | 135 +++++ FelLib/WinUSBNet/USBPipePolicy.cs | 181 +++++++ 29 files changed, 4353 insertions(+) create mode 100644 FelLib/AWFELMessage.cs create mode 100644 FelLib/AWFELStandardRequest.cs create mode 100644 FelLib/AWFELStatusResponse.cs create mode 100644 FelLib/AWFELVerifyDeviceResponse.cs create mode 100644 FelLib/AWUSBRequest.cs create mode 100644 FelLib/AWUSBResponse.cs create mode 100644 FelLib/Fel.cs create mode 100644 FelLib/FelException.cs create mode 100644 FelLib/FelParseException.cs create mode 100644 FelLib/WinUSBNet/API/APIException.cs create mode 100644 FelLib/WinUSBNet/API/DeviceDetails.cs create mode 100644 FelLib/WinUSBNet/API/DeviceManagement.cs create mode 100644 FelLib/WinUSBNet/API/DeviceManagementAPI.cs create mode 100644 FelLib/WinUSBNet/API/FileAPI.cs create mode 100644 FelLib/WinUSBNet/API/WinUSBDevice.cs create mode 100644 FelLib/WinUSBNet/API/WinUSBDeviceAPI.cs create mode 100644 FelLib/WinUSBNet/DeviceNotifyHook.cs create mode 100644 FelLib/WinUSBNet/USB.cs create mode 100644 FelLib/WinUSBNet/USBAsyncResult.cs create mode 100644 FelLib/WinUSBNet/USBDevice.cs create mode 100644 FelLib/WinUSBNet/USBDeviceDescriptor.cs create mode 100644 FelLib/WinUSBNet/USBDeviceInfo.cs create mode 100644 FelLib/WinUSBNet/USBException.cs create mode 100644 FelLib/WinUSBNet/USBInterface.cs create mode 100644 FelLib/WinUSBNet/USBInterfaceCollection.cs create mode 100644 FelLib/WinUSBNet/USBNotifier.cs create mode 100644 FelLib/WinUSBNet/USBPipe.cs create mode 100644 FelLib/WinUSBNet/USBPipeCollection.cs create mode 100644 FelLib/WinUSBNet/USBPipePolicy.cs (limited to 'FelLib') diff --git a/FelLib/AWFELMessage.cs b/FelLib/AWFELMessage.cs new file mode 100644 index 00000000..8fa179d0 --- /dev/null +++ b/FelLib/AWFELMessage.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace com.clusterrr.FelLib +{ + class AWFELMessage + { + public AWFELStandardRequest.RequestType Cmd; + public UInt16 Tag; + public UInt32 Address; + public UInt32 Len; + public UInt32 Flags; + + public AWFELMessage() + { + } + + public AWFELMessage(byte[] data) + { + Cmd = (AWFELStandardRequest.RequestType)(data[0] | (data[1] * 0x100)); + Tag = (UInt16)(data[2] | (data[3] * 0x100)); + Address = (UInt32)(data[4] | (data[5] * 0x100) | (data[6] * 0x10000) | (data[7] * 0x1000000)); + Len = (UInt32)(data[8] | (data[9] * 0x100) | (data[10] * 0x10000) | (data[11] * 0x1000000)); + Flags = (UInt32)(data[12] | (data[13] * 0x100) | (data[14] * 0x10000) | (data[15] * 0x1000000)); + } + + public byte[] Data + { + get + { + var data = new byte[16]; + data[0] = (byte)((UInt16)Cmd & 0xFF); // mark + data[1] = (byte)(((UInt16)Cmd >> 8) & 0xFF); // mark + data[2] = (byte)(Tag & 0xFF); // tag + data[3] = (byte)((Tag >> 8) & 0xFF); // tag + data[4] = (byte)(Address & 0xFF); // address + data[5] = (byte)((Address >> 8) & 0xFF); // address + data[6] = (byte)((Address >> 16) & 0xFF); // address + data[7] = (byte)((Address >> 24) & 0xFF); // address + data[8] = (byte)(Len & 0xFF); // len + data[9] = (byte)((Len >> 8) & 0xFF); // len + data[10] = (byte)((Len >> 16) & 0xFF); // len + data[11] = (byte)((Len >> 24) & 0xFF); // len + data[12] = (byte)(Flags & 0xFF); // flags + data[13] = (byte)((Flags >> 8) & 0xFF); // flags + data[14] = (byte)((Flags >> 16) & 0xFF); // flags + data[15] = (byte)((Flags >> 24) & 0xFF); // flags + + return data; + } + } + } +} diff --git a/FelLib/AWFELStandardRequest.cs b/FelLib/AWFELStandardRequest.cs new file mode 100644 index 00000000..fe892b1b --- /dev/null +++ b/FelLib/AWFELStandardRequest.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace com.clusterrr.FelLib +{ + public class AWFELStandardRequest + { + public enum RequestType + { + FEL_VERIFY_DEVICE = 0x1, // (Read length 32 => AWFELVerifyDeviceResponse) + FEL_SWITCH_ROLE = 0x2, + FEL_IS_READY = 0x3, // (Read length 8) + FEL_GET_CMD_SET_VER = 0x4, + FEL_DISCONNECT = 0x10, + FEL_DOWNLOAD = 0x101, // (Write data to the device) + FEL_RUN = 0x102, // (Execute code) + FEL_UPLOAD = 0x103, // (Read data from the device) + } + public RequestType Cmd; + public UInt16 Tag; + + public AWFELStandardRequest() + { + } + + public AWFELStandardRequest(byte[] data) + { + Cmd = (RequestType)(data[0] | (data[1] * 0x100)); + Tag = (UInt16)(data[2] | (data[3] * 0x100)); + } + + public byte[] Data + { + get + { + var data = new byte[16]; + data[0] = (byte)((UInt16)Cmd & 0xFF); // mark + data[1] = (byte)(((UInt16)Cmd >> 8) & 0xFF); // mark + data[2] = (byte)(Tag & 0xFF); // tag + data[3] = (byte)((Tag >> 8) & 0xFF); // tag + return data; + } + } + } +} diff --git a/FelLib/AWFELStatusResponse.cs b/FelLib/AWFELStatusResponse.cs new file mode 100644 index 00000000..a3d9ef59 --- /dev/null +++ b/FelLib/AWFELStatusResponse.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace com.clusterrr.FelLib +{ + public class AWFELStatusResponse + { + public UInt16 Mark = 0xFFFF; + public UInt16 Tag = 0; + public byte State; + + public AWFELStatusResponse() + { + } + + public AWFELStatusResponse(byte[] data) + { + Mark = (UInt16)(data[0] | (data[1] * 0x100)); + Tag = (UInt16)(data[2] | (data[3] * 0x100)); + State = data[4]; + } + + public byte[] Data + { + get + { + var data = new byte[8]; + data[0] = (byte)(Mark & 0xFF); // mark + data[1] = (byte)((Mark >> 8) & 0xFF); // mark + data[2] = (byte)(Tag & 0xFF); // tag + data[3] = (byte)((Tag >> 8) & 0xFF); // tag + data[4] = State; + return data; + } + } + } +} diff --git a/FelLib/AWFELVerifyDeviceResponse.cs b/FelLib/AWFELVerifyDeviceResponse.cs new file mode 100644 index 00000000..3d13ed6c --- /dev/null +++ b/FelLib/AWFELVerifyDeviceResponse.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace com.clusterrr.FelLib +{ + public class AWFELVerifyDeviceResponse + { + public UInt32 Board; + public UInt32 FW; + public UInt16 Mode; + public byte DataFlag; + public byte DataLength; + public UInt32 DataStartAddress; + + public AWFELVerifyDeviceResponse() + { + } + + public AWFELVerifyDeviceResponse(byte[] data) + { + if (data[0] != 'A' || data[1] != 'W' || data[2] != 'U' || data[3] != 'S' + || data[4] != 'B' || data[5] != 'F' || data[6] != 'E' || data[7] != 'X') + throw new FelParseException(); + Board = (UInt32)(data[8] | (data[9] * 0x100) | (data[10] * 0x10000) | (data[11] * 0x1000000)); + FW = (UInt32)(data[12] | (data[13] * 0x100) | (data[14] * 0x10000) | (data[15] * 0x1000000)); + Mode = (UInt16)(data[16] | (data[17] * 0x100)); + DataFlag = data[18]; + DataLength = data[19]; + DataStartAddress = (UInt32)(data[20] | (data[21] * 0x100) | (data[22] * 0x10000) | (data[23] * 0x1000000)); + } + + public byte[] Data + { + get + { + var data = new byte[32]; + data[0] = (byte)'A'; + data[1] = (byte)'W'; + data[2] = (byte)'U'; + data[3] = (byte)'S'; + data[4] = (byte)'B'; + data[5] = (byte)'F'; + data[6] = (byte)'E'; + data[7] = (byte)'X'; + data[8] = (byte)(Board & 0xFF); // board + data[9] = (byte)((Board >> 8) & 0xFF); // board + data[10] = (byte)((Board >> 16) & 0xFF); // board + data[11] = (byte)((Board >> 24) & 0xFF); // board + data[12] = (byte)(FW & 0xFF); // fw + data[13] = (byte)((FW >> 8) & 0xFF); // fw + data[14] = (byte)((FW >> 16) & 0xFF); // fw + data[15] = (byte)((FW >> 24) & 0xFF); // fw + data[16] = (byte)(Mode & 0xFF); // mode + data[17] = (byte)((Mode >> 8) & 0xFF); // mode + data[18] = DataFlag; + data[19] = DataLength; + data[20] = (byte)(DataStartAddress & 0xFF); // data_start_address + data[21] = (byte)((DataStartAddress >> 8) & 0xFF); // data_start_address + data[22] = (byte)((DataStartAddress >> 16) & 0xFF); // data_start_address + data[23] = (byte)((DataStartAddress >> 24) & 0xFF); // data_start_address + return data; + } + } + } +} diff --git a/FelLib/AWUSBRequest.cs b/FelLib/AWUSBRequest.cs new file mode 100644 index 00000000..6984c457 --- /dev/null +++ b/FelLib/AWUSBRequest.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace com.clusterrr.FelLib +{ + public class AWUSBRequest + { + public enum RequestType { AW_USB_READ = 0x11, AW_USB_WRITE = 0x12 }; + public UInt32 Tag = 0; + public UInt32 Len; + public RequestType Cmd; + public byte CmdLen = 0x0C; + + public AWUSBRequest() + { + } + + public AWUSBRequest(byte[] data) + { + if (data[0] != 'A' || data[1] != 'W' || data[2] != 'U' || data[3] != 'C') + throw new FelParseException(); + Tag = (UInt32)(data[4] | (data[5] * 0x100) | (data[6] * 0x10000) | (data[7] * 0x1000000)); + Len = (UInt32)(data[8] | (data[9] * 0x100) | (data[10] * 0x10000) | (data[11] * 0x1000000)); + CmdLen = data[15]; + Cmd = (RequestType)data[16]; + } + + public byte[] Data + { + get + { + var data = new byte[32]; + data[0] = (byte)'A'; + data[1] = (byte)'W'; + data[2] = (byte)'U'; + data[3] = (byte)'C'; + data[4] = (byte)(Tag & 0xFF); // tag + data[5] = (byte)((Tag >> 8) & 0xFF); // tag + data[6] = (byte)((Tag >> 16) & 0xFF); // tag + data[7] = (byte)((Tag >> 24) & 0xFF); // tag + data[8] = (byte)(Len & 0xFF); // len + data[9] = (byte)((Len >> 8) & 0xFF); // len + data[10] = (byte)((Len >> 16) & 0xFF); // len + data[11] = (byte)((Len >> 24) & 0xFF); // len + data[12] = data[13] = 0; // reserved1 + data[14] = 0; // reserved2 + data[15] = CmdLen; // cmd_len + data[16] = (byte)Cmd; + data[17] = 0; // reserved3 + data[18] = (byte)(Len & 0xFF); // len + data[19] = (byte)((Len >> 8) & 0xFF); // len + data[20] = (byte)((Len >> 16) & 0xFF); // len + data[21] = (byte)((Len >> 24) & 0xFF); // len + return data; + } + } + } +} diff --git a/FelLib/AWUSBResponse.cs b/FelLib/AWUSBResponse.cs new file mode 100644 index 00000000..44c1e05f --- /dev/null +++ b/FelLib/AWUSBResponse.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace com.clusterrr.FelLib +{ + class AWUSBResponse + { + public UInt32 Tag; + public UInt32 Residue; + public byte CswStatus; + + public AWUSBResponse() + { + } + + public AWUSBResponse(byte[] data) + { + if (data[0] != 'A' || data[1] != 'W' || data[2] != 'U' || data[3] != 'S') + throw new FelParseException(); + Tag = (UInt32)(data[4] | (data[5] * 0x100) | (data[6] * 0x10000) | (data[7] * 0x1000000)); + Residue = (UInt32)(data[8] | (data[9] * 0x100) | (data[10] * 0x10000) | (data[11] * 0x1000000)); + CswStatus = data[12]; + } + + public byte[] Data + { + get + { + var data = new byte[13]; + data[0] = (byte)'A'; + data[1] = (byte)'W'; + data[2] = (byte)'U'; + data[3] = (byte)'S'; + data[4] = (byte)(Tag & 0xFF); // tag + data[5] = (byte)((Tag >> 8) & 0xFF); // tag + data[6] = (byte)((Tag >> 16) & 0xFF); // tag + data[7] = (byte)((Tag >> 24) & 0xFF); // tag + data[8] = (byte)(Residue & 0xFF); // residue + data[9] = (byte)((Residue >> 8) & 0xFF); // residue + data[10] = (byte)((Residue >> 16) & 0xFF); // residue + data[11] = (byte)((Residue >> 24) & 0xFF); // residue + data[12] = CswStatus; // csw_status + return data; + } + } + + } +} diff --git a/FelLib/Fel.cs b/FelLib/Fel.cs new file mode 100644 index 00000000..f0db341d --- /dev/null +++ b/FelLib/Fel.cs @@ -0,0 +1,200 @@ +using MadWizard.WinUSBNet; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; + +namespace com.clusterrr.FelLib +{ + public class Fel + { + USBDevice device = null; + byte inEndp = 0; + byte outEndp = 0; + const int ReadTimeout = 500; + public const int MaxBulkSize = 0x10000; + UInt16 vid, pid; + + public static bool DeviceExists(UInt16 vid, UInt16 pid) + { + var fel = new Fel(); + try + { + fel.Open(vid, pid); + fel.VerifyDevice(); + return true; + } + catch + { + return false; + } + finally + { + fel.Close(); + } + } + + public void Open(UInt16 vid, UInt16 pid) + { + this.vid = vid; + this.pid = pid; + Close(); + device = USBDevice.GetSingleDevice(vid, pid); + if (device == null) throw new Exception("Device not found"); + foreach (var pipe in device.Pipes) + { + if (pipe.IsIn) + inEndp = pipe.Address; + else + outEndp = pipe.Address; + } + device.Pipes[outEndp].Policy.PipeTransferTimeout = ReadTimeout; + ClearInputBuffer(); + } + public void Close() + { + if (device != null) + { + device.Dispose(); + device = null; + } + } + + public void ClearInputBuffer() + { + var dummyBuff = new byte[64]; + device.Pipes[inEndp].Policy.PipeTransferTimeout = 50; + try + { + while (true) device.Pipes[inEndp].Read(dummyBuff); + } + catch { } + device.Pipes[inEndp].Policy.PipeTransferTimeout = ReadTimeout; + } + + private void WriteToUSB(byte[] buffer) + { + device.Pipes[outEndp].Write(buffer); + } + + private byte[] ReadFromUSB(UInt32 len) + { + var result = new List(); + while (result.Count < len) + { + var buffer = new byte[len - result.Count]; + var l = device.Pipes[inEndp].Read(buffer); + for (int i = 0; i < l && result.Count < len; i++) + result.Add(buffer[i]); + } + return result.ToArray(); + } + + private void FelWrite(byte[] buffer) + { + var req = new AWUSBRequest(); + req.Cmd = AWUSBRequest.RequestType.AW_USB_WRITE; + req.Len = (uint)buffer.Length; + WriteToUSB(req.Data); + WriteToUSB(buffer); + var resp = new AWUSBResponse(ReadFromUSB(13)); + if (resp.CswStatus != 0) throw new FelException("FEL write error"); + } + + private byte[] FelRead(UInt32 len) + { + var req = new AWUSBRequest(); + req.Cmd = AWUSBRequest.RequestType.AW_USB_READ; + req.Len = len; + WriteToUSB(req.Data); + + var result = ReadFromUSB(len); + var resp = new AWUSBResponse(ReadFromUSB(13)); + if (resp.CswStatus != 0) throw new FelException("FEL read error"); + return result; + } + + private void FelRequest(AWFELStandardRequest.RequestType command) + { + var req = new AWFELStandardRequest(); + req.Cmd = command; + FelWrite(req.Data); + } + + private void FelRequest(AWFELStandardRequest.RequestType command, UInt32 address, UInt32 length) + { + var req = new AWFELMessage(); + req.Cmd = command; + req.Address = address; + req.Len = length; + FelWrite(req.Data); + } + + public AWFELVerifyDeviceResponse VerifyDevice() + { + FelRequest(AWFELStandardRequest.RequestType.FEL_VERIFY_DEVICE); + var resp = FelRead(32); + var status = new AWFELStatusResponse(FelRead(8)); + return new AWFELVerifyDeviceResponse(resp); + } + + public void WriteMemory(UInt32 address, byte[] buffer) + { + int pos = 0; + while (pos < buffer.Length) + { + var buf = new byte[Math.Min(buffer.Length - pos, MaxBulkSize)]; + Array.Copy(buffer, pos, buf, 0, buf.Length); + + FelRequest(AWFELStandardRequest.RequestType.FEL_DOWNLOAD, (UInt32)(address + pos), (uint)buf.Length); + FelWrite(buf); + var status = new AWFELStatusResponse(FelRead(8)); + if (status.State != 0) throw new FelException("FEL write error"); + pos += buf.Length; + } + } + + public byte[] ReadMemory(UInt32 address, UInt32 length) + { + var result = new List(); + while (length > 0) + { + var l = Math.Min(length, MaxBulkSize); + Console.WriteLine("Reading {0:X8}, size: {1:X8}...", address, l); + FelRequest(AWFELStandardRequest.RequestType.FEL_UPLOAD, address, l); + var r = FelRead((UInt32)l); + result.AddRange(r); + var status = new AWFELStatusResponse(FelRead(8)); + if (status.State != 0) throw new FelException("FEL read error"); + length -= l; + address += l; + } + return result.ToArray(); + } + + public void Exec(UInt32 address, int pause = 0) + { + FelRequest(AWFELStandardRequest.RequestType.FEL_RUN, address, 0); + var status = new AWFELStatusResponse(FelRead(8)); + if (status.State != 0) throw new FelException("FEL run error"); + //Close(); + Thread.Sleep(pause * 1000); + //int errorCount = 0; + //while (true) + //{ + // try + // { + // Open(vid, pid); + // return; + // } + // catch (Exception ex) + // { + // errorCount++; + // if (errorCount >= 8) + // throw ex; + // } + //} + } + } +} diff --git a/FelLib/FelException.cs b/FelLib/FelException.cs new file mode 100644 index 00000000..b8a8eef8 --- /dev/null +++ b/FelLib/FelException.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace com.clusterrr.FelLib +{ + public class FelException : Exception + { + public FelException(string message) : base(message) + { + } + } +} diff --git a/FelLib/FelParseException.cs b/FelLib/FelParseException.cs new file mode 100644 index 00000000..0c93f924 --- /dev/null +++ b/FelLib/FelParseException.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace com.clusterrr.FelLib +{ + public class FelParseException : Exception + { + } +} diff --git a/FelLib/WinUSBNet/API/APIException.cs b/FelLib/WinUSBNet/API/APIException.cs new file mode 100644 index 00000000..8c9987a6 --- /dev/null +++ b/FelLib/WinUSBNet/API/APIException.cs @@ -0,0 +1,45 @@ +/* WinUSBNet library + * (C) 2010 Thomas Bleeker (www.madwizard.org) + * + * Licensed under the MIT license, see license.txt or: + * http://www.opensource.org/licenses/mit-license.php + */ + +using System; +using System.Collections.Generic; +using System.Text; +using System.Runtime; +using System.Runtime.InteropServices; +using System.ComponentModel; + + +namespace MadWizard.WinUSBNet.API +{ + /// + /// Exception used internally to catch Win32 API errors. This exception should + /// not be thrown to the library's caller. + /// + internal class APIException : Exception + { + public APIException(string message) : + base(message) + { + + } + public APIException(string message, Exception innerException) : base(message, innerException) + { + } + + public static APIException Win32(string message) + { + return APIException.Win32(message, Marshal.GetLastWin32Error()); + } + + public static APIException Win32(string message, int errorCode) + { + return new APIException(message, new Win32Exception(errorCode)); + + } + + } +} diff --git a/FelLib/WinUSBNet/API/DeviceDetails.cs b/FelLib/WinUSBNet/API/DeviceDetails.cs new file mode 100644 index 00000000..ada560c3 --- /dev/null +++ b/FelLib/WinUSBNet/API/DeviceDetails.cs @@ -0,0 +1,23 @@ +/* WinUSBNet library + * (C) 2010 Thomas Bleeker (www.madwizard.org) + * + * Licensed under the MIT license, see license.txt or: + * http://www.opensource.org/licenses/mit-license.php + */ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MadWizard.WinUSBNet.API +{ + internal struct DeviceDetails + { + public string DevicePath; + public string Manufacturer; + public string DeviceDescription; + public ushort VID; + public ushort PID; + } +} diff --git a/FelLib/WinUSBNet/API/DeviceManagement.cs b/FelLib/WinUSBNet/API/DeviceManagement.cs new file mode 100644 index 00000000..36a34e83 --- /dev/null +++ b/FelLib/WinUSBNet/API/DeviceManagement.cs @@ -0,0 +1,311 @@ +/* WinUSBNet library + * (C) 2010 Thomas Bleeker (www.madwizard.org) + * + * Licensed under the MIT license, see license.txt or: + * http://www.opensource.org/licenses/mit-license.php + */ + +/* NOTE: Parts of the code in this file are based on the work of Jan Axelson + * See http://www.lvr.com/winusb.htm for more information + */ + +using System; +using System.Runtime.InteropServices; +using System.Collections.Generic; +using System.Windows.Forms; +using System.Text.RegularExpressions; + +namespace MadWizard.WinUSBNet.API +{ + /// + /// Routines for detecting devices and receiving device notifications. + /// + internal static partial class DeviceManagement + { + + // Get device name from notification message. + // Also checks checkGuid with the GUID from the message to check the notification + // is for a relevant device. Other messages might be received. + public static string GetNotifyMessageDeviceName(Message m, Guid checkGuid) + { + int stringSize; + + + DEV_BROADCAST_DEVICEINTERFACE_1 devBroadcastDeviceInterface = new DEV_BROADCAST_DEVICEINTERFACE_1(); + DEV_BROADCAST_HDR devBroadcastHeader = new DEV_BROADCAST_HDR(); + + // The LParam parameter of Message is a pointer to a DEV_BROADCAST_HDR structure. + + Marshal.PtrToStructure(m.LParam, devBroadcastHeader); + + if ((devBroadcastHeader.dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE)) + { + // The dbch_devicetype parameter indicates that the event applies to a device interface. + // So the structure in LParam is actually a DEV_BROADCAST_INTERFACE structure, + // which begins with a DEV_BROADCAST_HDR. + + // Obtain the number of characters in dbch_name by subtracting the 32 bytes + // in the strucutre that are not part of dbch_name and dividing by 2 because there are + // 2 bytes per character. + + stringSize = System.Convert.ToInt32((devBroadcastHeader.dbch_size - 32) / 2); + + // The dbcc_name parameter of devBroadcastDeviceInterface contains the device name. + // Trim dbcc_name to match the size of the String. + + devBroadcastDeviceInterface.dbcc_name = new char[stringSize + 1]; + + // Marshal data from the unmanaged block pointed to by m.LParam + // to the managed object devBroadcastDeviceInterface. + + Marshal.PtrToStructure(m.LParam, devBroadcastDeviceInterface); + + // Check if message is for the GUID + if (devBroadcastDeviceInterface.dbcc_classguid != checkGuid) + return null; + + // Store the device name in a String. + string deviceNameString = new String(devBroadcastDeviceInterface.dbcc_name, 0, stringSize); + + return deviceNameString; + + } + return null; + } + + private static byte[] GetProperty(IntPtr deviceInfoSet, SP_DEVINFO_DATA deviceInfoData, SPDRP property, out int regType) + { + uint requiredSize; + + if (!SetupDiGetDeviceRegistryProperty(deviceInfoSet, ref deviceInfoData, property, IntPtr.Zero, IntPtr.Zero, 0, out requiredSize)) + { + if (Marshal.GetLastWin32Error() != ERROR_INSUFFICIENT_BUFFER) + throw APIException.Win32("Failed to get buffer size for device registry property."); + } + + byte[] buffer = new byte[requiredSize]; + + if (!SetupDiGetDeviceRegistryProperty(deviceInfoSet, ref deviceInfoData, property, out regType, buffer, (uint)buffer.Length, out requiredSize)) + throw APIException.Win32("Failed to get device registry property."); + + return buffer; + + + + } + + // todo: is the queried data always available, or should we check ERROR_INVALID_DATA? + private static string GetStringProperty(IntPtr deviceInfoSet, SP_DEVINFO_DATA deviceInfoData, SPDRP property) + { + int regType; + byte[] buffer = GetProperty(deviceInfoSet, deviceInfoData, property, out regType); + if (regType != (int)RegTypes.REG_SZ) + throw new APIException("Invalid registry type returned for device property."); + + // sizof(char), 2 bytes, are removed to leave out the string terminator + return System.Text.Encoding.Unicode.GetString(buffer, 0, buffer.Length - sizeof(char)); + } + + private static string[] GetMultiStringProperty(IntPtr deviceInfoSet, SP_DEVINFO_DATA deviceInfoData, SPDRP property) + { + int regType; + byte[] buffer = GetProperty(deviceInfoSet, deviceInfoData, property, out regType); + if (regType != (int)RegTypes.REG_MULTI_SZ) + throw new APIException("Invalid registry type returned for device property."); + + string fullString = System.Text.Encoding.Unicode.GetString(buffer); + + return fullString.Split(new char[] { '\0' }, StringSplitOptions.RemoveEmptyEntries); + + } + private static DeviceDetails GetDeviceDetails(string devicePath, IntPtr deviceInfoSet, SP_DEVINFO_DATA deviceInfoData) + { + DeviceDetails details = new DeviceDetails(); + details.DevicePath = devicePath; + details.DeviceDescription = GetStringProperty(deviceInfoSet, deviceInfoData, SPDRP.SPDRP_DEVICEDESC); + details.Manufacturer = GetStringProperty(deviceInfoSet, deviceInfoData, SPDRP.SPDRP_MFG); + string[] hardwareIDs = GetMultiStringProperty(deviceInfoSet, deviceInfoData, SPDRP.SPDRP_HARDWAREID); + + Regex regex = new Regex("^USB\\\\VID_([0-9A-F]{4})&PID_([0-9A-F]{4})", RegexOptions.IgnoreCase); + bool foundVidPid = false; + foreach (string hardwareID in hardwareIDs) + { + Match match = regex.Match(hardwareID); + if (match.Success) + { + details.VID = ushort.Parse(match.Groups[1].Value, System.Globalization.NumberStyles.AllowHexSpecifier); + details.PID = ushort.Parse(match.Groups[2].Value, System.Globalization.NumberStyles.AllowHexSpecifier); + foundVidPid = true; + break; + } + } + + if (!foundVidPid) + throw new APIException("Failed to find VID and PID for USB device. No hardware ID could be parsed."); + + return details; + } + + + public static DeviceDetails[] FindDevices(UInt16 vid, UInt16 pid) + { + IntPtr deviceInfoSet = IntPtr.Zero; + List deviceList = new List(); + var guid = new Guid("{A5DCBF10-6530-11D2-901F-00C04FB951ED}"); // USB device + try + { + deviceInfoSet = SetupDiGetClassDevs(IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, + DIGCF_PRESENT | DIGCF_DEVICEINTERFACE | DIGCF_ALLCLASSES); + if (deviceInfoSet == FileIO.INVALID_HANDLE_VALUE) + throw APIException.Win32("Failed to enumerate devices."); + int memberIndex = 0; + while (true) + { + // Begin with 0 and increment through the device information set until + // no more devices are available. + SP_DEVICE_INTERFACE_DATA deviceInterfaceData = new SP_DEVICE_INTERFACE_DATA(); + + // The cbSize element of the deviceInterfaceData structure must be set to + // the structure's size in bytes. + // The size is 28 bytes for 32-bit code and 32 bytes for 64-bit code. + deviceInterfaceData.cbSize = Marshal.SizeOf(deviceInterfaceData); + + bool success; + + success = SetupDiEnumDeviceInterfaces(deviceInfoSet, IntPtr.Zero, ref guid, memberIndex, ref deviceInterfaceData); + + // Find out if a device information set was retrieved. + if (!success) + { + int lastError = Marshal.GetLastWin32Error(); + if (lastError == ERROR_NO_MORE_ITEMS) + break; + + throw APIException.Win32("Failed to get device interface."); + } + // A device is present. + + int bufferSize = 0; + + success = SetupDiGetDeviceInterfaceDetail + (deviceInfoSet, + ref deviceInterfaceData, + IntPtr.Zero, + 0, + ref bufferSize, + IntPtr.Zero); + + if (!success) + { + if (Marshal.GetLastWin32Error() != ERROR_INSUFFICIENT_BUFFER) + throw APIException.Win32("Failed to get interface details buffer size."); + } + + IntPtr detailDataBuffer = IntPtr.Zero; + try + { + + // Allocate memory for the SP_DEVICE_INTERFACE_DETAIL_DATA structure using the returned buffer size. + detailDataBuffer = Marshal.AllocHGlobal(bufferSize); + + // Store cbSize in the first bytes of the array. The number of bytes varies with 32- and 64-bit systems. + + Marshal.WriteInt32(detailDataBuffer, (IntPtr.Size == 4) ? (4 + Marshal.SystemDefaultCharSize) : 8); + + // Call SetupDiGetDeviceInterfaceDetail again. + // This time, pass a pointer to DetailDataBuffer + // and the returned required buffer size. + + // build a DevInfo Data structure + SP_DEVINFO_DATA da = new SP_DEVINFO_DATA(); + da.cbSize = Marshal.SizeOf(da); + + + success = SetupDiGetDeviceInterfaceDetail + (deviceInfoSet, + ref deviceInterfaceData, + detailDataBuffer, + bufferSize, + ref bufferSize, + ref da); + + if (!success) + throw APIException.Win32("Failed to get device interface details."); + + + // Skip over cbsize (4 bytes) to get the address of the devicePathName. + + IntPtr pDevicePathName = new IntPtr(detailDataBuffer.ToInt64() + 4); + string pathName = Marshal.PtrToStringUni(pDevicePathName); + + // Get the String containing the devicePathName. + + DeviceDetails details = GetDeviceDetails(pathName, deviceInfoSet, da); + + if (details.VID == vid && details.PID == pid) + deviceList.Add(details); + } + finally + { + if (detailDataBuffer != IntPtr.Zero) + { + Marshal.FreeHGlobal(detailDataBuffer); + detailDataBuffer = IntPtr.Zero; + } + } + memberIndex++; + } + } + finally + { + if (deviceInfoSet != IntPtr.Zero && deviceInfoSet != FileIO.INVALID_HANDLE_VALUE) + { + SetupDiDestroyDeviceInfoList(deviceInfoSet); + } + } + return deviceList.ToArray(); + } + + + public static void RegisterForDeviceNotifications(IntPtr controlHandle, Guid classGuid, ref IntPtr deviceNotificationHandle) + { + + DEV_BROADCAST_DEVICEINTERFACE devBroadcastDeviceInterface = new DEV_BROADCAST_DEVICEINTERFACE(); + IntPtr devBroadcastDeviceInterfaceBuffer = IntPtr.Zero; + try + { + int size = Marshal.SizeOf(devBroadcastDeviceInterface); + devBroadcastDeviceInterface.dbcc_size = size; + + devBroadcastDeviceInterface.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; + devBroadcastDeviceInterface.dbcc_reserved = 0; + devBroadcastDeviceInterface.dbcc_classguid = classGuid; + devBroadcastDeviceInterfaceBuffer = Marshal.AllocHGlobal(size); + + // Copy the DEV_BROADCAST_DEVICEINTERFACE structure to the buffer. + // Set fDeleteOld True to prevent memory leaks. + Marshal.StructureToPtr(devBroadcastDeviceInterface, devBroadcastDeviceInterfaceBuffer, true); + + deviceNotificationHandle = RegisterDeviceNotification(controlHandle, devBroadcastDeviceInterfaceBuffer, DEVICE_NOTIFY_WINDOW_HANDLE); + if (deviceNotificationHandle == IntPtr.Zero) + throw APIException.Win32("Failed to register device notification"); + + // Marshal data from the unmanaged block devBroadcastDeviceInterfaceBuffer to + // the managed object devBroadcastDeviceInterface + Marshal.PtrToStructure(devBroadcastDeviceInterfaceBuffer, devBroadcastDeviceInterface); + } + finally + { + // Free the memory allocated previously by AllocHGlobal. + if (devBroadcastDeviceInterfaceBuffer != IntPtr.Zero) + Marshal.FreeHGlobal(devBroadcastDeviceInterfaceBuffer); + } + } + + public static void StopDeviceDeviceNotifications(IntPtr deviceNotificationHandle) + { + if (!DeviceManagement.UnregisterDeviceNotification(deviceNotificationHandle)) + throw APIException.Win32("Failed to unregister device notification"); + } + } +} diff --git a/FelLib/WinUSBNet/API/DeviceManagementAPI.cs b/FelLib/WinUSBNet/API/DeviceManagementAPI.cs new file mode 100644 index 00000000..00ebfc69 --- /dev/null +++ b/FelLib/WinUSBNet/API/DeviceManagementAPI.cs @@ -0,0 +1,159 @@ +/* WinUSBNet library + * (C) 2010 Thomas Bleeker (www.madwizard.org) + * + * Licensed under the MIT license, see license.txt or: + * http://www.opensource.org/licenses/mit-license.php + */ + +/* NOTE: Parts of the code in this file are based on the work of Jan Axelson + * See http://www.lvr.com/winusb.htm for more information + */ + +using System; +using System.Runtime.InteropServices; + +namespace MadWizard.WinUSBNet.API +{ + /// + /// API declarations relating to device management (SetupDixxx and + /// RegisterDeviceNotification functions). + /// + + internal static partial class DeviceManagement + { + // from dbt.h + + internal const Int32 DBT_DEVICEARRIVAL = 0X8000; + internal const Int32 DBT_DEVICEREMOVECOMPLETE = 0X8004; + private const Int32 DBT_DEVTYP_DEVICEINTERFACE = 5; + private const Int32 DBT_DEVTYP_HANDLE = 6; + private const Int32 DEVICE_NOTIFY_ALL_INTERFACE_CLASSES = 4; + private const Int32 DEVICE_NOTIFY_SERVICE_HANDLE = 1; + private const Int32 DEVICE_NOTIFY_WINDOW_HANDLE = 0; + internal const Int32 WM_DEVICECHANGE = 0X219; + + // from setupapi.h + + private const Int32 DIGCF_PRESENT = 2; + private const Int32 DIGCF_DEVICEINTERFACE = 0X10; + private const Int32 DIGCF_ALLCLASSES = 0X04; + + // Two declarations for the DEV_BROADCAST_DEVICEINTERFACE structure. + + // Use this one in the call to RegisterDeviceNotification() and + // in checking dbch_devicetype in a DEV_BROADCAST_HDR structure: + + [StructLayout(LayoutKind.Sequential)] + private class DEV_BROADCAST_DEVICEINTERFACE + { + internal Int32 dbcc_size; + internal Int32 dbcc_devicetype; + internal Int32 dbcc_reserved; + internal Guid dbcc_classguid; + internal Int16 dbcc_name; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + private class DEV_BROADCAST_DEVICEINTERFACE_1 + { + internal Int32 dbcc_size; + internal Int32 dbcc_devicetype; + internal Int32 dbcc_reserved; + internal Guid dbcc_classguid; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 255)] + internal Char[] dbcc_name; + } + + [StructLayout(LayoutKind.Sequential)] + private class DEV_BROADCAST_HDR + { + internal Int32 dbch_size; + internal Int32 dbch_devicetype; + internal Int32 dbch_reserved; + } + + private struct SP_DEVICE_INTERFACE_DATA + { + internal Int32 cbSize; + internal System.Guid InterfaceClassGuid; + internal Int32 Flags; + internal IntPtr Reserved; + } + private struct SP_DEVINFO_DATA + { + internal Int32 cbSize; + internal System.Guid ClassGuid; + internal Int32 DevInst; + internal IntPtr Reserved; + } + + // from pinvoke.net + private enum SPDRP : uint + { + SPDRP_DEVICEDESC = 0x00000000, + SPDRP_HARDWAREID = 0x00000001, + SPDRP_COMPATIBLEIDS = 0x00000002, + SPDRP_NTDEVICEPATHS = 0x00000003, + SPDRP_SERVICE = 0x00000004, + SPDRP_CONFIGURATION = 0x00000005, + SPDRP_CONFIGURATIONVECTOR = 0x00000006, + SPDRP_CLASS = 0x00000007, + SPDRP_CLASSGUID = 0x00000008, + SPDRP_DRIVER = 0x00000009, + SPDRP_CONFIGFLAGS = 0x0000000A, + SPDRP_MFG = 0x0000000B, + SPDRP_FRIENDLYNAME = 0x0000000C, + SPDRP_LOCATION_INFORMATION = 0x0000000D, + SPDRP_PHYSICAL_DEVICE_OBJECT_NAME = 0x0000000E, + SPDRP_CAPABILITIES = 0x0000000F, + SPDRP_UI_NUMBER = 0x00000010, + SPDRP_UPPERFILTERS = 0x00000011, + SPDRP_LOWERFILTERS = 0x00000012, + SPDRP_MAXIMUM_PROPERTY = 0x00000013, + + SPDRP_ENUMERATOR_NAME = 0x16, + }; + + + private enum RegTypes : int + { + // incomplete list, these are just the ones used. + REG_SZ = 1, + REG_MULTI_SZ = 7 + } + + + [DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)] + private static extern IntPtr RegisterDeviceNotification(IntPtr hRecipient, IntPtr NotificationFilter, Int32 Flags); + + //[DllImport("setupapi.dll", SetLastError = true)] + //internal static extern Int32 SetupDiCreateDeviceInfoList(ref System.Guid ClassGuid, Int32 hwndParent); + + [DllImport("setupapi.dll", SetLastError = true)] + private static extern Int32 SetupDiDestroyDeviceInfoList(IntPtr DeviceInfoSet); + + [DllImport("setupapi.dll", SetLastError = true)] + private static extern bool SetupDiEnumDeviceInterfaces(IntPtr DeviceInfoSet, IntPtr DeviceInfoData, ref System.Guid InterfaceClassGuid, Int32 MemberIndex, ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData); + + [DllImport("setupapi.dll", CharSet = CharSet.Unicode, SetLastError = true)] + private static extern bool SetupDiGetDeviceRegistryProperty(IntPtr DeviceInfoSet, ref SP_DEVINFO_DATA DeviceInfoData, SPDRP Property, out int PropertyRegDataType, byte[] PropertyBuffer, uint PropertyBufferSize, out UInt32 RequiredSize); + + [DllImport("setupapi.dll", CharSet = CharSet.Unicode, SetLastError = true)] + private static extern bool SetupDiGetDeviceRegistryProperty(IntPtr DeviceInfoSet, ref SP_DEVINFO_DATA DeviceInfoData, SPDRP Property, IntPtr PropertyRegDataType, IntPtr PropertyBuffer, uint PropertyBufferSize, out UInt32 RequiredSize); + + [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Unicode)] + private static extern IntPtr SetupDiGetClassDevs(IntPtr ClassGuid, IntPtr Enumerator, IntPtr hwndParent, Int32 Flags); + + [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Unicode)] + private static extern bool SetupDiGetDeviceInterfaceDetail(IntPtr DeviceInfoSet, ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData, IntPtr DeviceInterfaceDetailData, Int32 DeviceInterfaceDetailDataSize, ref Int32 RequiredSize, ref SP_DEVINFO_DATA DeviceInfoData); + + [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Unicode)] + private static extern bool SetupDiGetDeviceInterfaceDetail(IntPtr DeviceInfoSet, ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData, IntPtr DeviceInterfaceDetailData, Int32 DeviceInterfaceDetailDataSize, ref Int32 RequiredSize, IntPtr DeviceInfoData); + + [DllImport("user32.dll", SetLastError = true)] + private static extern bool UnregisterDeviceNotification(IntPtr Handle); + + private const int ERROR_NO_MORE_ITEMS = 259; + private const int ERROR_INSUFFICIENT_BUFFER = 122; + } +} diff --git a/FelLib/WinUSBNet/API/FileAPI.cs b/FelLib/WinUSBNet/API/FileAPI.cs new file mode 100644 index 00000000..f785c9c4 --- /dev/null +++ b/FelLib/WinUSBNet/API/FileAPI.cs @@ -0,0 +1,39 @@ +/* WinUSBNet library + * (C) 2010 Thomas Bleeker (www.madwizard.org) + * + * Licensed under the MIT license, see license.txt or: + * http://www.opensource.org/licenses/mit-license.php + */ + +/* NOTE: Parts of the code in this file are based on the work of Jan Axelson + * See http://www.lvr.com/winusb.htm for more information + */ + +using System; +using Microsoft.Win32.SafeHandles; +using System.Runtime.InteropServices; + +namespace MadWizard.WinUSBNet.API +{ + /// + /// API declarations relating to file I/O (and used by WinUsb). + /// + + sealed internal class FileIO + { + public const Int32 FILE_ATTRIBUTE_NORMAL = 0X80; + public const Int32 FILE_FLAG_OVERLAPPED = 0X40000000; + public const Int32 FILE_SHARE_READ = 1; + public const Int32 FILE_SHARE_WRITE = 2; + public const UInt32 GENERIC_READ = 0X80000000; + public const UInt32 GENERIC_WRITE = 0X40000000; + public static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1); + public const Int32 OPEN_EXISTING = 3; + + public const Int32 ERROR_IO_PENDING = 997; + + [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + public static extern SafeFileHandle CreateFile(String lpFileName, UInt32 dwDesiredAccess, Int32 dwShareMode, IntPtr lpSecurityAttributes, Int32 dwCreationDisposition, Int32 dwFlagsAndAttributes, Int32 hTemplateFile); + } + +} diff --git a/FelLib/WinUSBNet/API/WinUSBDevice.cs b/FelLib/WinUSBNet/API/WinUSBDevice.cs new file mode 100644 index 00000000..93a9ad9c --- /dev/null +++ b/FelLib/WinUSBNet/API/WinUSBDevice.cs @@ -0,0 +1,477 @@ +/* WinUSBNet library + * (C) 2010 Thomas Bleeker (www.madwizard.org) + * + * Licensed under the MIT license, see license.txt or: + * http://www.opensource.org/licenses/mit-license.php + */ + +/* NOTE: Parts of the code in this file are based on the work of Jan Axelson + * See http://www.lvr.com/winusb.htm for more information + */ + +using System; +using Microsoft.Win32.SafeHandles; +using System.Runtime.InteropServices; +using System.Collections.Generic; +using System.Threading; +using System.ComponentModel; + +namespace MadWizard.WinUSBNet.API +{ + /// + /// Wrapper for a WinUSB device dealing with the WinUSB and additional interface handles + /// + partial class WinUSBDevice : IDisposable + { + private bool _disposed = false; + private SafeFileHandle _deviceHandle; + private IntPtr _winUsbHandle = IntPtr.Zero; + private IntPtr[] _addInterfaces = null; + public WinUSBDevice() + { + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + ~WinUSBDevice() + { + Dispose(false); + } + + private void CheckNotDisposed() + { + if (_disposed) + throw new ObjectDisposedException("USB device object has been disposed."); + } + + // TODO: check if not disposed on methods (although this is already checked by USBDevice) + + protected virtual void Dispose(bool disposing) + { + if (_disposed) + return; + + if (disposing) + { + // Dispose managed resources + if (_deviceHandle != null && !_deviceHandle.IsInvalid) + _deviceHandle.Dispose(); + _deviceHandle = null; + } + + // Dispose unmanaged resources + FreeWinUSB(); + _disposed = true; + } + private void FreeWinUSB() + { + if (_addInterfaces != null) + { + for (int i = 0; i < _addInterfaces.Length; i++) + { + WinUsb_Free(_addInterfaces[i]); + } + _addInterfaces = null; + } + if (_winUsbHandle != IntPtr.Zero) + WinUsb_Free(_winUsbHandle); + _winUsbHandle = IntPtr.Zero; + } + + public USB_DEVICE_DESCRIPTOR GetDeviceDescriptor() + { + USB_DEVICE_DESCRIPTOR deviceDesc; + uint transfered; + uint size = (uint)Marshal.SizeOf(typeof(USB_DEVICE_DESCRIPTOR)); + bool success = WinUsb_GetDescriptor(_winUsbHandle, USB_DEVICE_DESCRIPTOR_TYPE, + 0, 0, out deviceDesc, size, out transfered); + if (!success) + throw APIException.Win32("Failed to get USB device descriptor."); + + if (transfered != size) + throw APIException.Win32("Incomplete USB device descriptor."); + + return deviceDesc; + } + + private int ReadStringDescriptor(byte index, ushort languageID, byte[] buffer) + { + uint transfered; + bool success = WinUsb_GetDescriptor(_winUsbHandle, USB_STRING_DESCRIPTOR_TYPE, + index, languageID, buffer, (uint)buffer.Length, out transfered); + if (!success) + throw APIException.Win32("Failed to get USB string descriptor (" + index + ")."); + + if (transfered == 0) + throw new APIException("No data returned when reading USB descriptor."); + + int length = buffer[0]; + if (length > transfered) + throw new APIException("Unexpected length when reading USB descriptor."); + return length; + } + + public ushort[] GetSupportedLanguageIDs() + { + byte[] buffer = new byte[256]; + int length = ReadStringDescriptor(0, 0, buffer); + length -= 2; // Skip length byte and descriptor type + if (length < 0 || (length % 2) != 0) + throw new APIException("Unexpected length when reading supported languages."); + + ushort[] langIDs = new ushort[length / 2]; + Buffer.BlockCopy(buffer, 2, langIDs, 0, length); + return langIDs; + } + + public string GetStringDescriptor(byte index, ushort languageID) + { + byte[] buffer = new byte[256]; + int length = ReadStringDescriptor(index, languageID, buffer); + length -= 2; // Skip length byte and descriptor type + if (length < 0) + return null; + char[] chars = System.Text.Encoding.Unicode.GetChars(buffer, 2, length); + return new string(chars); + } + + public void ControlTransfer(byte requestType, byte request, ushort value, ushort index, ushort length, byte[] data) + { + uint bytesReturned = 0; + WINUSB_SETUP_PACKET setupPacket; + + setupPacket.RequestType = requestType; + setupPacket.Request = request; + setupPacket.Value = value; + setupPacket.Index = index; + setupPacket.Length = length; + + bool success = WinUsb_ControlTransfer(_winUsbHandle, setupPacket, data, length, ref bytesReturned, IntPtr.Zero); + if (!success) // todo check bytes returned? + throw APIException.Win32("Control transfer on WinUSB device failed."); + } + + + public void OpenDevice(string devicePathName) + { + try + { + _deviceHandle = FileIO.CreateFile(devicePathName, + (FileIO.GENERIC_WRITE | FileIO.GENERIC_READ), + FileIO.FILE_SHARE_READ | FileIO.FILE_SHARE_WRITE, + IntPtr.Zero, + FileIO.OPEN_EXISTING, + FileIO.FILE_ATTRIBUTE_NORMAL | FileIO.FILE_FLAG_OVERLAPPED, + 0); + if (_deviceHandle.IsInvalid) + throw APIException.Win32("Failed to open WinUSB device handle."); + InitializeDevice(); + + } + catch(Exception) + { + if (_deviceHandle != null) + { + _deviceHandle.Dispose(); + _deviceHandle = null; + } + FreeWinUSB(); + throw; + } + } + + private IntPtr InterfaceHandle(int index) + { + if (index == 0) + return _winUsbHandle; + return _addInterfaces[index - 1]; + } + + public int InterfaceCount + { + get + { + return 1 + (_addInterfaces == null ? 0 : _addInterfaces.Length); + } + } + + public void GetInterfaceInfo(int interfaceIndex, out USB_INTERFACE_DESCRIPTOR descriptor, out WINUSB_PIPE_INFORMATION[] pipes) + { + var pipeList = new List(); + bool success = WinUsb_QueryInterfaceSettings(InterfaceHandle(interfaceIndex), 0, out descriptor); + if (!success) + throw APIException.Win32("Failed to get WinUSB device interface descriptor."); + + IntPtr interfaceHandle = InterfaceHandle(interfaceIndex); + for (byte pipeIdx = 0; pipeIdx < descriptor.bNumEndpoints; pipeIdx++) + { + WINUSB_PIPE_INFORMATION pipeInfo; + success = WinUsb_QueryPipe(interfaceHandle, 0, pipeIdx, out pipeInfo); + + pipeList.Add(pipeInfo); + if (!success) + throw APIException.Win32("Failed to get WinUSB device pipe information."); + } + pipes = pipeList.ToArray(); + + } + private void InitializeDevice() + { + bool success; + + success = WinUsb_Initialize(_deviceHandle, ref _winUsbHandle); + + if (!success) + throw APIException.Win32("Failed to initialize WinUSB handle. Device might not be connected."); + + List interfaces = new List(); + byte numAddInterfaces = 0; + byte idx = 0; + + try + { + while (true) + { + IntPtr ifaceHandle = IntPtr.Zero; + success = WinUsb_GetAssociatedInterface(_winUsbHandle, idx, out ifaceHandle); + if (!success) + { + if (Marshal.GetLastWin32Error() == ERROR_NO_MORE_ITEMS) + break; + + throw APIException.Win32("Failed to enumerate interfaces for WinUSB device."); + } + interfaces.Add(ifaceHandle); + idx++; + numAddInterfaces++; + } + } + finally + { + // Save interface handles (will be cleaned by Dispose) + // also in case of exception (which is why it is in finally block), + // because some handles might have already been opened and need + // to be disposed. + _addInterfaces = interfaces.ToArray(); + } + + // Bind handle (needed for overlapped I/O thread pool) + ThreadPool.BindHandle(_deviceHandle); + // TODO: bind interface handles as well? doesn't seem to be necessary + } + + public void ReadPipe(int ifaceIndex, byte pipeID, byte[] buffer, int offset, int bytesToRead, out uint bytesRead) + { + bool success; + unsafe + { + fixed (byte* pBuffer = buffer) + { + success = WinUsb_ReadPipe(InterfaceHandle(ifaceIndex), pipeID, pBuffer + offset, (uint)bytesToRead, + out bytesRead, IntPtr.Zero); + } + } + if (!success) + throw APIException.Win32("Failed to read pipe on WinUSB device."); + } + + private unsafe void HandleOverlappedAPI(bool success, string errorMessage, NativeOverlapped* pOverlapped, USBAsyncResult result, int bytesTransfered) + { + if (!success) + { + if (Marshal.GetLastWin32Error() != FileIO.ERROR_IO_PENDING) + { + Overlapped.Unpack(pOverlapped); + Overlapped.Free(pOverlapped); + throw APIException.Win32(errorMessage); + } + } + else + { + // Immediate success! + Overlapped.Unpack(pOverlapped); + Overlapped.Free(pOverlapped); + + result.OnCompletion(true, null, bytesTransfered, false); + // is the callback still called in this case?? todo + } + + } + + public void ReadPipeOverlapped(int ifaceIndex, byte pipeID, byte[] buffer, int offset, int bytesToRead, USBAsyncResult result) + { + Overlapped overlapped = new Overlapped(); + + overlapped.AsyncResult = result; + + unsafe + { + NativeOverlapped* pOverlapped = null; + uint bytesRead; + + pOverlapped = overlapped.Pack(PipeIOCallback, buffer); + bool success; + // Buffer is pinned already by overlapped.Pack + fixed (byte* pBuffer = buffer) + { + success = WinUsb_ReadPipe(InterfaceHandle(ifaceIndex), pipeID, pBuffer + offset, (uint)bytesToRead, + out bytesRead, pOverlapped); + } + HandleOverlappedAPI(success, "Failed to asynchronously read pipe on WinUSB device.", pOverlapped, result, (int)bytesRead); + } + } + + public void WriteOverlapped(int ifaceIndex, byte pipeID, byte[] buffer, int offset, int bytesToWrite, USBAsyncResult result) + { + Overlapped overlapped = new Overlapped(); + overlapped.AsyncResult = result; + + unsafe + { + NativeOverlapped* pOverlapped = null; + + uint bytesWritten; + pOverlapped = overlapped.Pack(PipeIOCallback, buffer); + + bool success; + // Buffer is pinned already by overlapped.Pack + fixed (byte* pBuffer = buffer) + { + success = WinUsb_WritePipe(InterfaceHandle(ifaceIndex), pipeID, pBuffer + offset, (uint)bytesToWrite, + out bytesWritten, pOverlapped); + } + HandleOverlappedAPI(success, "Failed to asynchronously write pipe on WinUSB device.", pOverlapped, result, (int)bytesWritten); + + } + } + + + + public void ControlTransferOverlapped(byte requestType, byte request, ushort value, ushort index, ushort length, byte[] data, USBAsyncResult result) + { + uint bytesReturned = 0; + WINUSB_SETUP_PACKET setupPacket; + + setupPacket.RequestType = requestType; + setupPacket.Request = request; + setupPacket.Value = value; + setupPacket.Index = index; + setupPacket.Length = length; + + Overlapped overlapped = new Overlapped(); + overlapped.AsyncResult = result; + + unsafe + { + NativeOverlapped* pOverlapped = null; + pOverlapped = overlapped.Pack(PipeIOCallback, data); + bool success = WinUsb_ControlTransfer(_winUsbHandle, setupPacket, data, length, ref bytesReturned, pOverlapped); + HandleOverlappedAPI(success, "Asynchronous control transfer on WinUSB device failed.", pOverlapped, result, (int)bytesReturned); + } + } + + private unsafe void PipeIOCallback(uint errorCode, uint numBytes, NativeOverlapped* pOverlapped) + { + try + { + Exception error = null; + if (errorCode != 0) + { + error = APIException.Win32("Asynchronous operation on WinUSB device failed.", (int)errorCode); + } + Overlapped overlapped = Overlapped.Unpack(pOverlapped); + USBAsyncResult result = (USBAsyncResult)overlapped.AsyncResult; + Overlapped.Free(pOverlapped); + pOverlapped = null; + + result.OnCompletion(false, error, (int)numBytes, true); + } + finally + { + if (pOverlapped != null) + { + Overlapped.Unpack(pOverlapped); + Overlapped.Free(pOverlapped); + } + } + } + public void AbortPipe(int ifaceIndex, byte pipeID) + { + bool success = WinUsb_AbortPipe(InterfaceHandle(ifaceIndex), pipeID); + if (!success) + throw APIException.Win32("Failed to abort pipe on WinUSB device."); + + } + + public void WritePipe(int ifaceIndex, byte pipeID, byte[] buffer, int offset, int length) + { + uint bytesWritten; + bool success; + unsafe + { + fixed (byte* pBuffer = buffer) + { + success = WinUsb_WritePipe(InterfaceHandle(ifaceIndex), pipeID, pBuffer + offset, (uint)length, + out bytesWritten, IntPtr.Zero); + } + } + if (!success || (bytesWritten != length)) + throw APIException.Win32("Failed to write pipe on WinUSB device."); + + } + + public void FlushPipe(int ifaceIndex, byte pipeID) + { + bool success = WinUsb_FlushPipe(InterfaceHandle(ifaceIndex), pipeID); + if (!success) + throw APIException.Win32("Failed to flush pipe on WinUSB device."); + } + + public void SetPipePolicy(int ifaceIndex, byte pipeID, POLICY_TYPE policyType, bool value) + { + byte byteVal = (byte)(value ? 1 : 0); + bool success = WinUsb_SetPipePolicy(InterfaceHandle(ifaceIndex), pipeID, (uint)policyType, 1, ref byteVal); + if (!success) + throw APIException.Win32("Failed to set WinUSB pipe policy."); + } + + + public void SetPipePolicy(int ifaceIndex, byte pipeID, POLICY_TYPE policyType, uint value) + { + + bool success = WinUsb_SetPipePolicy(InterfaceHandle(ifaceIndex), pipeID, (uint)policyType, 4, ref value); + + if (!success) + throw APIException.Win32("Failed to set WinUSB pipe policy."); + } + + + public bool GetPipePolicyBool(int ifaceIndex, byte pipeID, POLICY_TYPE policyType) + { + byte result; + uint length = 1; + + bool success = WinUsb_GetPipePolicy(InterfaceHandle(ifaceIndex), pipeID, (uint)policyType, ref length, out result); + if (!success || length != 1) + throw APIException.Win32("Failed to get WinUSB pipe policy."); + return result != 0; + } + + + public uint GetPipePolicyUInt(int ifaceIndex, byte pipeID, POLICY_TYPE policyType) + { + + uint result; + uint length = 4; + bool success = WinUsb_GetPipePolicy(InterfaceHandle(ifaceIndex), pipeID, (uint)policyType, ref length, out result); + + if (!success || length != 4) + throw APIException.Win32("Failed to get WinUSB pipe policy."); + return result; + } + } +} diff --git a/FelLib/WinUSBNet/API/WinUSBDeviceAPI.cs b/FelLib/WinUSBNet/API/WinUSBDeviceAPI.cs new file mode 100644 index 00000000..0492b7fd --- /dev/null +++ b/FelLib/WinUSBNet/API/WinUSBDeviceAPI.cs @@ -0,0 +1,202 @@ +/* WinUSBNet library + * (C) 2010 Thomas Bleeker (www.madwizard.org) + * + * Licensed under the MIT license, see license.txt or: + * http://www.opensource.org/licenses/mit-license.php + */ + +/* NOTE: Parts of the code in this file are based on the work of Jan Axelson + * See http://www.lvr.com/winusb.htm for more information + */ + +using System; +using Microsoft.Win32.SafeHandles; +using System.Runtime.InteropServices; +using System.Threading; + +namespace MadWizard.WinUSBNet.API +{ + [StructLayout(LayoutKind.Sequential)] + struct USB_DEVICE_DESCRIPTOR + { + public byte bLength; + public byte bDescriptorType; + public ushort bcdUSB; + public byte bDeviceClass; + public byte bDeviceSubClass; + public byte bDeviceProtocol; + public byte bMaxPacketSize0; + public ushort idVendor; + public ushort idProduct; + public ushort bcdDevice; + public byte iManufacturer; + public byte iProduct; + public byte iSerialNumber; + public byte bNumConfigurations; + }; + + [StructLayout(LayoutKind.Sequential)] + struct USB_CONFIGURATION_DESCRIPTOR + { + public byte bLength; + public byte bDescriptorType; + public ushort wTotalLength; + public byte bNumInterfaces; + public byte bConfigurationValue; + public byte iConfiguration; + public byte bmAttributes; + public byte MaxPower; + } + + [StructLayout(LayoutKind.Sequential)] + struct USB_INTERFACE_DESCRIPTOR + { + public byte bLength; + public byte bDescriptorType; + public byte bInterfaceNumber; + public byte bAlternateSetting; + public byte bNumEndpoints; + public byte bInterfaceClass; + public byte bInterfaceSubClass; + public byte bInterfaceProtocol; + public byte iInterface; + }; + enum USBD_PIPE_TYPE : int + { + UsbdPipeTypeControl, + UsbdPipeTypeIsochronous, + UsbdPipeTypeBulk, + UsbdPipeTypeInterrupt, + } + [StructLayout(LayoutKind.Sequential)] + struct WINUSB_PIPE_INFORMATION + { + public USBD_PIPE_TYPE PipeType; + public byte PipeId; + public ushort MaximumPacketSize; + public byte Interval; + } + + enum POLICY_TYPE : int + { + SHORT_PACKET_TERMINATE = 1, + AUTO_CLEAR_STALL, + PIPE_TRANSFER_TIMEOUT, + IGNORE_SHORT_PACKETS, + ALLOW_PARTIAL_READS, + AUTO_FLUSH, + RAW_IO, + } + + + partial class WinUSBDevice + { + private const UInt32 DEVICE_SPEED = ((UInt32)(1)); + + private enum USB_DEVICE_SPEED : int + { + UsbLowSpeed = 1, + UsbFullSpeed, + UsbHighSpeed, + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + private struct WINUSB_SETUP_PACKET + { + public byte RequestType; + public byte Request; + public ushort Value; + public ushort Index; + public ushort Length; + } + + [DllImport("winusb.dll", SetLastError = true)] + private static extern bool WinUsb_ControlTransfer(IntPtr InterfaceHandle, WINUSB_SETUP_PACKET SetupPacket, Byte[] Buffer, UInt32 BufferLength, ref UInt32 LengthTransferred, IntPtr Overlapped); + + [DllImport("winusb.dll", SetLastError = true)] + private static unsafe extern bool WinUsb_ControlTransfer(IntPtr InterfaceHandle, WINUSB_SETUP_PACKET SetupPacket, Byte[] Buffer, UInt32 BufferLength, ref UInt32 LengthTransferred, NativeOverlapped* pOverlapped); + + [DllImport("winusb.dll", SetLastError = true)] + private static extern bool WinUsb_Free(IntPtr InterfaceHandle); + + [DllImport("winusb.dll", SetLastError = true)] + private static extern bool WinUsb_Initialize(SafeFileHandle DeviceHandle, ref IntPtr InterfaceHandle); + + [DllImport("winusb.dll", SetLastError = true)] + private static extern bool WinUsb_QueryDeviceInformation(IntPtr InterfaceHandle, UInt32 InformationType, ref UInt32 BufferLength, out byte Buffer); + + [DllImport("winusb.dll", SetLastError = true)] + private static extern bool WinUsb_QueryInterfaceSettings(IntPtr InterfaceHandle, Byte AlternateInterfaceNumber, out USB_INTERFACE_DESCRIPTOR UsbAltInterfaceDescriptor); + + [DllImport("winusb.dll", SetLastError = true)] + private static extern bool WinUsb_QueryPipe(IntPtr InterfaceHandle, Byte AlternateInterfaceNumber, Byte PipeIndex, out WINUSB_PIPE_INFORMATION PipeInformation); + + [DllImport("winusb.dll", SetLastError = true)] + private static unsafe extern bool WinUsb_ReadPipe(IntPtr InterfaceHandle, byte PipeID, byte* pBuffer, uint BufferLength, out uint LengthTransferred, IntPtr Overlapped); + + [DllImport("winusb.dll", SetLastError = true)] + private static unsafe extern bool WinUsb_ReadPipe(IntPtr InterfaceHandle, byte PipeID, byte* pBuffer, uint BufferLength, out uint LengthTransferred, NativeOverlapped* pOverlapped); + + [DllImport("winusb.dll", SetLastError = true)] + private static extern bool WinUsb_AbortPipe(IntPtr InterfaceHandle, byte PipeID); + + // Two declarations for WinUsb_SetPipePolicy. + // Use this one when the returned Value is a Byte (all except PIPE_TRANSFER_TIMEOUT): + + [DllImport("winusb.dll", SetLastError = true)] + private static extern bool WinUsb_SetPipePolicy(IntPtr InterfaceHandle, Byte PipeID, UInt32 PolicyType, UInt32 ValueLength, ref byte Value); + + [DllImport("winusb.dll", SetLastError = true)] + private static extern bool WinUsb_GetPipePolicy(IntPtr InterfaceHandle, Byte PipeID, UInt32 PolicyType, ref UInt32 ValueLength, out byte Value); + + // Use this alias when the returned Value is a UInt32 (PIPE_TRANSFER_TIMEOUT only): + + [DllImport("winusb.dll", SetLastError = true)] + private static extern bool WinUsb_SetPipePolicy(IntPtr InterfaceHandle, Byte PipeID, UInt32 PolicyType, UInt32 ValueLength, ref UInt32 Value); + + [DllImport("winusb.dll", SetLastError = true)] + private static extern bool WinUsb_GetPipePolicy(IntPtr InterfaceHandle, Byte PipeID, UInt32 PolicyType, ref UInt32 ValueLength, out UInt32 Value); + + [DllImport("winusb.dll", SetLastError = true)] + private static unsafe extern bool WinUsb_WritePipe(IntPtr InterfaceHandle, byte PipeID, byte* pBuffer, uint BufferLength, out uint LengthTransferred, IntPtr Overlapped); + + [DllImport("winusb.dll", SetLastError = true)] + private static unsafe extern bool WinUsb_WritePipe(IntPtr InterfaceHandle, byte PipeID, byte* pBuffer, uint BufferLength, out uint LengthTransferred, NativeOverlapped* pOverlapped); + + + [DllImport("kernel32.dll", SetLastError = true)] + private static unsafe extern bool CancelIo(IntPtr hFile); + + [DllImport("kernel32.dll", SetLastError = true)] + private static unsafe extern bool CancelIoEx(IntPtr hFile, NativeOverlapped* pOverlapped); + + + + + [DllImport("winusb.dll", SetLastError = true)] + private static extern bool WinUsb_FlushPipe(IntPtr InterfaceHandle, byte PipeID); + + [DllImport("winusb.dll", SetLastError = true)] + private static extern bool WinUsb_GetDescriptor(IntPtr InterfaceHandle, byte DescriptorType, + byte Index, UInt16 LanguageID, byte[] Buffer, UInt32 BufferLength, out UInt32 LengthTransfered); + + [DllImport("winusb.dll", SetLastError = true)] + private static extern bool WinUsb_GetDescriptor(IntPtr InterfaceHandle, byte DescriptorType, + byte Index, UInt16 LanguageID, out USB_DEVICE_DESCRIPTOR deviceDesc, UInt32 BufferLength, out UInt32 LengthTransfered); + + [DllImport("winusb.dll", SetLastError = true)] + private static extern bool WinUsb_GetDescriptor(IntPtr InterfaceHandle, byte DescriptorType, + byte Index, UInt16 LanguageID, out USB_CONFIGURATION_DESCRIPTOR deviceDesc, UInt32 BufferLength, out UInt32 LengthTransfered); + + [DllImport("winusb.dll", SetLastError = true)] + private static extern bool WinUsb_GetAssociatedInterface(IntPtr InterfaceHandle, byte AssociatedInterfaceIndex, + out IntPtr AssociatedInterfaceHandle); + + private const int USB_DEVICE_DESCRIPTOR_TYPE = 0x01; + private const int USB_CONFIGURATION_DESCRIPTOR_TYPE = 0x02; + private const int USB_STRING_DESCRIPTOR_TYPE = 0x03; + + private const int ERROR_NO_MORE_ITEMS = 259; + + } +} diff --git a/FelLib/WinUSBNet/DeviceNotifyHook.cs b/FelLib/WinUSBNet/DeviceNotifyHook.cs new file mode 100644 index 00000000..99d6fb04 --- /dev/null +++ b/FelLib/WinUSBNet/DeviceNotifyHook.cs @@ -0,0 +1,117 @@ +/* WinUSBNet library + * (C) 2010 Thomas Bleeker (www.madwizard.org) + * + * Licensed under the MIT license, see license.txt or: + * http://www.opensource.org/licenses/mit-license.php + */ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace MadWizard.WinUSBNet +{ + internal class DeviceNotifyHook : NativeWindow, IDisposable + { + // http://msdn.microsoft.com/en-us/library/system.windows.forms.nativewindow.aspx + + // TODO: disposed exception when disposed + + private Control _parent; + private USBNotifier _notifier; + private Guid _guid; + private IntPtr _notifyHandle; + + public DeviceNotifyHook(USBNotifier notifier, Control parent, Guid guid) + { + _parent = parent; + _guid = guid; + _parent.HandleCreated += new EventHandler(this.OnHandleCreated); + _parent.HandleDestroyed += new EventHandler(this.OnHandleDestroyed); + _notifier = notifier; + } + + ~DeviceNotifyHook() + { + Dispose(false); + } + + // Listen for the control's window creation and then hook into it. + internal void OnHandleCreated(object sender, EventArgs e) + { + try + { + // Window is now created, assign handle to NativeWindow. + IntPtr handle = ((Control)sender).Handle; + AssignHandle(handle); + + if (_notifyHandle != IntPtr.Zero) + { + API.DeviceManagement.StopDeviceDeviceNotifications(_notifyHandle); + _notifyHandle = IntPtr.Zero; + } + API.DeviceManagement.RegisterForDeviceNotifications(handle, _guid, ref _notifyHandle); + } + catch (API.APIException ex) + { + throw new USBException("Failed to register new window handle for device notification.", ex); + } + } + + internal void OnHandleDestroyed(object sender, EventArgs e) + { + try + { + // Window was destroyed, release hook. + ReleaseHandle(); + if (_notifyHandle != IntPtr.Zero) + { + API.DeviceManagement.StopDeviceDeviceNotifications(_notifyHandle); + _notifyHandle = IntPtr.Zero; + } + } + catch (API.APIException ex) + { + throw new USBException("Failed to unregister destroyed window handle for device notification.", ex); + } + } + + + protected override void WndProc(ref Message m) + { + // Listen for operating system messages + + switch (m.Msg) + { + case API.DeviceManagement.WM_DEVICECHANGE: + _notifier.HandleDeviceChange(m); + break; + } + base.WndProc(ref m); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + // clean managed resources + + // do not clean the notifier here. the notifier owns and will dispose this object. + } + if (_notifyHandle != IntPtr.Zero) + { + API.DeviceManagement.StopDeviceDeviceNotifications(_notifyHandle); + _notifyHandle = IntPtr.Zero; + } + } + + } +} diff --git a/FelLib/WinUSBNet/USB.cs b/FelLib/WinUSBNet/USB.cs new file mode 100644 index 00000000..0875faf2 --- /dev/null +++ b/FelLib/WinUSBNet/USB.cs @@ -0,0 +1,80 @@ +/* WinUSBNet library + * (C) 2010 Thomas Bleeker (www.madwizard.org) + * + * Licensed under the MIT license, see license.txt or: + * http://www.opensource.org/licenses/mit-license.php + */ + +using System; + + +namespace MadWizard.WinUSBNet +{ + /// + /// USB base class code enumeration, as defined in the USB specification + /// + public enum USBBaseClass + { + /// Unknown non-zero class code. Used when the actual class code + /// does not match any of the ones defined in this enumeration. + Unknown = -1, + + /// Base class defined elsewhere (0x00) + None = 0x00, + + /// Audio base class (0x01) + Audio = 0x01, + + /// Communications and CDC control base class (0x02) + CommCDC = 0x02, + + /// HID base class (0x03) + HID = 0x03, + + /// Physical base class (0x05) + Physical = 0x05, + + /// Image base class (0x06) + Image = 0x06, + + /// Printer base class (0x07) + Printer = 0x07, + + /// Mass storage base class (0x08) + MassStorage = 0x08, + + /// Hub base class (0x09) + Hub = 0x09, + + /// CDC data base class (0x0A) + CDCData = 0x0A, + + /// Smart card base class (0x0B) + SmartCard = 0x0B, + + /// Content security base class (0x0D) + ContentSecurity = 0x0D, + + /// Video base class (0x0E) + Video = 0x0E, + + /// Personal healthcare base class (0x0F) + PersonalHealthcare = 0x0F, + + /// Diagnosticdevice base class (0xDC) + DiagnosticDevice = 0xDC, + + /// Wireless controller base class (0xE0) + WirelessController = 0xE0, + + /// Miscellaneous base class (0xEF) + Miscellaneous = 0xEF, + + /// Application specific base class (0xFE) + ApplicationSpecific = 0xFE, + + /// Vendor specific base class (0xFF) + VendorSpecific = 0xFF, + }; + +} \ No newline at end of file diff --git a/FelLib/WinUSBNet/USBAsyncResult.cs b/FelLib/WinUSBNet/USBAsyncResult.cs new file mode 100644 index 00000000..6cb5dcef --- /dev/null +++ b/FelLib/WinUSBNet/USBAsyncResult.cs @@ -0,0 +1,136 @@ +/* WinUSBNet library + * (C) 2010 Thomas Bleeker (www.madwizard.org) + * + * Licensed under the MIT license, see license.txt or: + * http://www.opensource.org/licenses/mit-license.php + */ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +namespace MadWizard.WinUSBNet +{ + internal class USBAsyncResult : IAsyncResult, IDisposable + { + private object _stateObject; + private AsyncCallback _userCallback; + private bool _completed; + private bool _completedSynchronously; + private ManualResetEvent _waitEvent; + private int _bytesTransfered; + private Exception _error; + + public USBAsyncResult(AsyncCallback userCallback, object stateObject) + { + _stateObject = stateObject; + _userCallback = userCallback; + _completedSynchronously = false; + _completed = false; + _waitEvent = null; + } + + public object AsyncState + { + get + { + return _stateObject; + } + } + + public Exception Error + { + get + { + lock (this) + { + return _error; + } + } + } + public int BytesTransfered + { + get { return _bytesTransfered; } + } + public WaitHandle AsyncWaitHandle + { + get + { + lock (this) + { + if (_waitEvent == null) + _waitEvent = new ManualResetEvent(_completed); + } + return _waitEvent; + } + } + + public bool CompletedSynchronously + { + get + { + lock (this) + { + return _completedSynchronously; + } + } + } + + public bool IsCompleted + { + get + { + lock (this) + { + return _completed; + } + } + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + public void OnCompletion(bool completedSynchronously, Exception error, int bytesTransfered, bool synchronousCallback) + { + lock (this) + { + _completedSynchronously = completedSynchronously; + _completed = true; + _error = error; + _bytesTransfered = bytesTransfered; + if (_waitEvent != null) + _waitEvent.Set(); + } + if (_userCallback != null) + { + if (synchronousCallback) + RunCallback(null); + else + ThreadPool.QueueUserWorkItem(RunCallback); + } + + } + private void RunCallback(object state) + { + _userCallback(this); + } + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + // Cleanup managed resources + lock (this) + { + if (_waitEvent != null) + _waitEvent.Close(); + } + } + } + + + } +} diff --git a/FelLib/WinUSBNet/USBDevice.cs b/FelLib/WinUSBNet/USBDevice.cs new file mode 100644 index 00000000..d4ef5668 --- /dev/null +++ b/FelLib/WinUSBNet/USBDevice.cs @@ -0,0 +1,756 @@ +/* WinUSBNet library + * (C) 2010 Thomas Bleeker (www.madwizard.org) + * + * Licensed under the MIT license, see license.txt or: + * http://www.opensource.org/licenses/mit-license.php + */ + +using System; +using System.Collections.Generic; +using System.Text; + +namespace MadWizard.WinUSBNet +{ + /// + /// The UsbDevice class represents a single WinUSB device. + /// + public class USBDevice : IDisposable + { + private API.WinUSBDevice _wuDevice = null; + private bool _disposed = false; + + /// + /// Collection of all pipes available on the USB device + /// + public USBPipeCollection Pipes + { + get; + private set; + } + + + + + /// + /// Collection of all interfaces available on the USB device + /// + public USBInterfaceCollection Interfaces + { + get; + private set; + } + + /// + /// Device descriptor with information about the device + /// + public USBDeviceDescriptor Descriptor + { + get; + private set; + } + + /// + /// Constructs a new USB device + /// + /// USB device info of the device to create + public USBDevice(USBDeviceInfo deviceInfo) + : this(deviceInfo.DevicePath) + { + // Handled in other constructor + } + + /// + /// Disposes the UsbDevice including all unmanaged WinUSB handles. This function + /// should be called when the UsbDevice object is no longer in use, otherwise + /// unmanaged handles will remain open until the garbage collector finalizes the + /// object. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Finalizer for the UsbDevice. Disposes all unmanaged handles. + /// + ~USBDevice() + { + Dispose(false); + } + + /// + /// Disposes the object + /// + /// Indicates wether Dispose was called manually (true) or by + /// the garbage collector (false) via the destructor. + protected virtual void Dispose(bool disposing) + { + if (_disposed) + return; + + if (disposing) + { + if (_wuDevice != null) + _wuDevice.Dispose(); + } + + // Clean unmanaged resources here. + // (none currently) + + _disposed = true; + } + + /// + /// Constructs a new USB device + /// + /// Device path name of the USB device to create + public USBDevice(string devicePathName) + { + try + { + Descriptor = GetDeviceDescriptor(devicePathName); + } + catch + { + Descriptor = null; + } + _wuDevice = new API.WinUSBDevice(); + try + { + _wuDevice.OpenDevice(devicePathName); + InitializeInterfaces(); + } + catch (API.APIException e) + { + _wuDevice.Dispose(); + throw new USBException("Failed to open device.", e); + } + } + + internal API.WinUSBDevice InternalDevice + { + get + { + return _wuDevice; + } + } + + private void InitializeInterfaces() + { + int numInterfaces = _wuDevice.InterfaceCount; + + List allPipes = new List(); + + USBInterface[] interfaces = new USBInterface[numInterfaces]; + // UsbEndpoint + for (int i = 0; i < numInterfaces; i++) + { + API.USB_INTERFACE_DESCRIPTOR descriptor; + API.WINUSB_PIPE_INFORMATION[] pipesInfo; + _wuDevice.GetInterfaceInfo(i, out descriptor, out pipesInfo); + USBPipe[] interfacePipes = new USBPipe[pipesInfo.Length]; + for (int k = 0; k < pipesInfo.Length; k++) + { + USBPipe pipe = new USBPipe(this, pipesInfo[k]); + interfacePipes[k] = pipe; + allPipes.Add(pipe); + } + // TODO: + //if (descriptor.iInterface != 0) + // _wuDevice.GetStringDescriptor(descriptor.iInterface); + USBPipeCollection pipeCollection = new USBPipeCollection(interfacePipes); + interfaces[i] = new USBInterface(this, i, descriptor, pipeCollection); + } + Pipes = new USBPipeCollection(allPipes.ToArray()); + Interfaces = new USBInterfaceCollection(interfaces); + } + + private void CheckControlParams(int value, int index, byte[] buffer, int length) + { + if (value < ushort.MinValue || value > ushort.MaxValue) + throw new ArgumentOutOfRangeException("Value parameter out of range."); + if (index < ushort.MinValue || index > ushort.MaxValue) + throw new ArgumentOutOfRangeException("Index parameter out of range."); + if (length > buffer.Length) + throw new ArgumentOutOfRangeException("Length parameter is larger than the size of the buffer."); + if (length > ushort.MaxValue) + throw new ArgumentOutOfRangeException("Length too large"); + } + + /// + /// Specifies the timeout in milliseconds for control pipe operations. If a control transfer does not finish within the specified time it will fail. + /// When set to zero, no timeout is used. Default value is 5000 milliseconds. + /// + /// WinUSB_GetPipePolicy for a more detailed description + public int ControlPipeTimeout + { + get + { + return (int)_wuDevice.GetPipePolicyUInt(0, 0x00, API.POLICY_TYPE.PIPE_TRANSFER_TIMEOUT); + } + set + { + if (value < 0) + throw new ArgumentOutOfRangeException("Control pipe timeout cannot be negative."); + _wuDevice.SetPipePolicy(0, 0x00, API.POLICY_TYPE.PIPE_TRANSFER_TIMEOUT, (uint)value); + } + } + + + /// + /// Initiates a control transfer over the default control endpoint. This method allows both IN and OUT direction transfers, depending + /// on the highest bit of the parameter. Alternatively, and + /// can be used for control transfers in a specific direction, which is the recommended way because + /// it prevents using the wrong direction accidentally. Use the ControlTransfer method when the direction is not known at compile time. + /// + /// The setup packet request type. + /// The setup packet device request. + /// The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF). + /// The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF). + /// The data to transfer in the data stage of the control. When the transfer is in the IN direction the data received will be + /// written to this buffer. For an OUT direction transfer the contents of the buffer are written sent through the pipe. + /// Length of the data to transfer. Must be equal to or less than the length of . + /// The setup packet's length member will be set to this length. + public void ControlTransfer(byte requestType, byte request, int value, int index, byte[] buffer, int length) + { + // Parameters are int and not ushort because ushort is not CLS compliant. + CheckNotDisposed(); + CheckControlParams(value, index, buffer, length); + + try + { + _wuDevice.ControlTransfer(requestType, request, (ushort)value, (ushort)index, (ushort)length, buffer); + } + catch (API.APIException e) + { + throw new USBException("Control transfer failed", e); + } + } + + /// + /// Initiates an asynchronous control transfer over the default control endpoint. This method allows both IN and OUT direction transfers, depending + /// on the highest bit of the parameter. Alternatively, and + /// can be used for asynchronous control transfers in a specific direction, which is + /// the recommended way because it prevents using the wrong direction accidentally. Use the BeginControlTransfer method when the direction is not + /// known at compile time. + /// The setup packet request type. + /// The setup packet device request. + /// The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF). + /// The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF). + /// The data to transfer in the data stage of the control. When the transfer is in the IN direction the data received will be + /// written to this buffer. For an OUT direction transfer the contents of the buffer are written sent through the pipe. Note: This buffer is not allowed + /// to change for the duration of the asynchronous operation. + /// Length of the data to transfer. Must be equal to or less than the length of . The setup packet's length member will be set to this length. + /// An optional asynchronous callback, to be called when the control transfer is complete. Can be null if no callback is required. + /// A user-provided object that distinguishes this particular asynchronous operation. Can be null if not required. + /// An object repesenting the asynchronous control transfer, which could still be pending. + /// This method always completes immediately even if the operation is still pending. The object returned represents the operation + /// and must be passed to to retrieve the result of the operation. For every call to this method a matching call to + /// must be made. When specifies a callback function, this function will be called when the operation is completed. The optional + /// parameter can be used to pass user-defined information to this callback or the . The + /// also provides an event handle () that will be triggered when the operation is complete as well. + /// + public IAsyncResult BeginControlTransfer(byte requestType, byte request, int value, int index, byte[] buffer, int length, AsyncCallback userCallback, object stateObject) + { + // Parameters are int and not ushort because ushort is not CLS compliant. + CheckNotDisposed(); + CheckControlParams(value, index, buffer, length); + + USBAsyncResult result = new USBAsyncResult(userCallback, stateObject); + + try + { + _wuDevice.ControlTransferOverlapped(requestType, request, (ushort)value, (ushort)index, (ushort)length, buffer, result); + } + catch (API.APIException e) + { + if (result != null) + result.Dispose(); + throw new USBException("Asynchronous control transfer failed", e); + } + catch (Exception) + { + if (result != null) + result.Dispose(); + throw; + } + return result; + } + + /// + /// Initiates an asynchronous control transfer over the default control endpoint. This method allows both IN and OUT direction transfers, depending + /// on the highest bit of the parameter. Alternatively, and + /// can be used for asynchronous control transfers in a specific direction, which is + /// the recommended way because it prevents using the wrong direction accidentally. Use the BeginControlTransfer method when the direction is not + /// known at compile time. + /// The setup packet request type. + /// The setup packet device request. + /// The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF). + /// The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF). + /// The data to transfer in the data stage of the control. When the transfer is in the IN direction the data received will be + /// written to this buffer. For an OUT direction transfer the contents of the buffer are written sent through the pipe. The setup packet's length member will + /// be set to the length of this buffer. Note: This buffer is not allowed to change for the duration of the asynchronous operation. + /// An optional asynchronous callback, to be called when the control transfer is complete. Can be null if no callback is required. + /// A user-provided object that distinguishes this particular asynchronous operation. Can be null if not required. + /// An object repesenting the asynchronous control transfer, which could still be pending. + /// This method always completes immediately even if the operation is still pending. The object returned represents the operation + /// and must be passed to to retrieve the result of the operation. For every call to this method a matching call to + /// must be made. When specifies a callback function, this function will be called when the operation is completed. The optional + /// parameter can be used to pass user-defined information to this callback or the . The + /// also provides an event handle () that will be triggered when the operation is complete as well. + /// + public IAsyncResult BeginControlTransfer(byte requestType, byte request, int value, int index, byte[] buffer, AsyncCallback userCallback, object stateObject) + { + return BeginControlTransfer(requestType, request, value, index, buffer, buffer.Length, userCallback, stateObject); + } + + + /// + /// Waits for a pending asynchronous control transfer to complete. + /// + /// The object representing the asynchonous operation, + /// as returned by one of the ControlIn, ControlOut or ControlTransfer methods. + /// The number of bytes transfered during the operation. + /// Every asynchronous control transfer must have a matching call to to dispose + /// of any resources used and to retrieve the result of the operation. When the operation was successful the method returns the number + /// of bytes that were transfered. If an error occurred during the operation this method will throw the exceptions that would + /// otherwise have ocurred during the operation. If the operation is not yet finished EndControlTransfer will wait for the + /// operation to finish before returning. + public int EndControlTransfer(IAsyncResult asyncResult) + { + if (asyncResult == null) + throw new NullReferenceException("asyncResult cannot be null"); + if (!(asyncResult is USBAsyncResult)) + throw new ArgumentException("AsyncResult object was not created by calling one of the BeginControl* methods on this class."); + + // todo: check duplicate end control + USBAsyncResult result = (USBAsyncResult)asyncResult; + try + { + if (!result.IsCompleted) + result.AsyncWaitHandle.WaitOne(); + + if (result.Error != null) + throw new USBException("Asynchronous control transfer from pipe has failed.", result.Error); + + return result.BytesTransfered; + } + finally + { + result.Dispose(); + } + + } + + + /// + /// Initiates a control transfer over the default control endpoint. This method allows both IN and OUT direction transfers, depending + /// on the highest bit of the parameter). Alternatively, and + /// can be used for control transfers in a specific direction, which is the recommended way because + /// it prevents using the wrong direction accidentally. Use the ControlTransfer method when the direction is not known at compile time. + /// + /// The setup packet request type. + /// The setup packet device request. + /// The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF). + /// The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF). + /// The data to transfer in the data stage of the control. When the transfer is in the IN direction the data received will be + /// written to this buffer. For an OUT direction transfer the contents of the buffer are written sent through the pipe. The length of this + /// buffer is used as the number of bytes in the control transfer. The setup packet's length member will be set to this length as well. + public void ControlTransfer(byte requestType, byte request, int value, int index, byte[] buffer) + { + ControlTransfer(requestType, request, value, index, buffer, buffer.Length); + } + + /// + /// Initiates a control transfer without a data stage over the default control endpoint. This method allows both IN and OUT direction transfers, depending + /// on the highest bit of the parameter). Alternatively, and + /// can be used for control transfers in a specific direction, which is the recommended way because + /// it prevents using the wrong direction accidentally. Use the ControlTransfer method when the direction is not known at compile time. + /// + /// The setup packet request type. + /// The setup packet device request. + /// The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF). + /// The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF). + public void ControlTransfer(byte requestType, byte request, int value, int index) + { + // TODO: null instead of empty buffer. But overlapped code would have to be fixed for this (no buffer to pin) + ControlTransfer(requestType, request, value, index, new byte[0], 0); + } + + private void CheckIn(byte requestType) + { + if ((requestType & 0x80) == 0) // Host to device? + throw new ArgumentException("Request type is not IN."); + } + + private void CheckOut(byte requestType) + { + if ((requestType & 0x80) == 0x80) // Device to host? + throw new ArgumentException("Request type is not OUT."); + } + + /// + /// Initiates a control transfer over the default control endpoint. The request should have an IN direction (specified by the highest bit + /// of the parameter). A buffer to receive the data is automatically created by this method. + /// + /// The setup packet request type. The request type must specify the IN direction (highest bit set). + /// The setup packet device request. + /// The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF). + /// The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF). + /// Length of the data to transfer. A buffer will be created with this length and the length member of the setup packet + /// will be set to this length. + /// A buffer containing the data transfered. + public byte[] ControlIn(byte requestType, byte request, int value, int index, int length) + { + CheckIn(requestType); + byte[] buffer = new byte[length]; + ControlTransfer(requestType, request, value, index, buffer, buffer.Length); + return buffer; + } + + + /// + /// Initiates a control transfer over the default control endpoint. The request should have an IN direction (specified by the highest bit + /// of the parameter). + /// + /// The setup packet request type. The request type must specify the IN direction (highest bit set). + /// The setup packet device request. + /// The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF). + /// The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF). + /// The buffer that will receive the data transfered. + /// Length of the data to transfer. The length member of the setup packet will be set to this length. The buffer specified + /// by the parameter should have at least this length. + public void ControlIn(byte requestType, byte request, int value, int index, byte[] buffer, int length) + { + CheckIn(requestType); + ControlTransfer(requestType, request, value, index, buffer, length); + } + + /// + /// Initiates a control transfer over the default control endpoint. The request should have an IN direction (specified by the highest bit + /// of the parameter). The length of buffer given by the parameter will dictate + /// the number of bytes that are transfered and the value of the setup packet's length member. + /// + /// The setup packet request type. The request type must specify the IN direction (highest bit set). + /// The setup packet device request. + /// The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF). + /// The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF). + /// The buffer that will receive the data transfered. The length of this buffer will be the number of bytes transfered. + public void ControlIn(byte requestType, byte request, int value, int index, byte[] buffer) + { + CheckIn(requestType); + ControlTransfer(requestType, request, value, index, buffer); + } + + /// + /// Initiates a control transfer without a data stage over the default control endpoint. The request should have an IN direction (specified by the highest bit + /// of the parameter). The setup packets' length member will be set to zero. + /// + /// The setup packet request type. The request type must specify the IN direction (highest bit set). + /// The setup packet device request. + /// The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF). + /// The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF). + public void ControlIn(byte requestType, byte request, int value, int index) + { + CheckIn(requestType); + // TODO: null instead of empty buffer. But overlapped code would have to be fixed for this (no buffer to pin) + ControlTransfer(requestType, request, value, index, new byte[0]); + } + + /// + /// Initiates a control transfer over the default control endpoint. The request should have an OUT direction (specified by the highest bit + /// of the parameter). + /// + /// The setup packet request type. The request type must specify the OUT direction (highest bit cleared). + /// The setup packet device request. + /// The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF). + /// The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF). + /// A buffer containing the data to transfer in the data stage. + /// Length of the data to transfer. Only the first bytes of will be transfered. + /// The setup packet's length parameter is set to this length. + public void ControlOut(byte requestType, byte request, int value, int index, byte[] buffer, int length) + { + CheckOut(requestType); + ControlTransfer(requestType, request, value, index, buffer, length); + } + + /// + /// Initiates a control transfer over the default control endpoint. The request should have an OUT direction (specified by the highest bit + /// of the parameter). + /// + /// The setup packet request type. The request type must specify the OUT direction (highest bit cleared). + /// The setup packet device request. + /// The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF). + /// The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF). + /// A buffer containing the data to transfer in the data stage. The complete buffer is transfered. The setup packet's length + /// parameter is set to the length of this buffer. + public void ControlOut(byte requestType, byte request, int value, int index, byte[] buffer) + { + CheckOut(requestType); + ControlTransfer(requestType, request, value, index, buffer); + } + + /// + /// Initiates a control transfer without a data stage over the default control endpoint. The request should have an OUT direction (specified by the highest bit + /// of the parameter. The setup packets' length member will be set to zero. + /// + /// The setup packet request type. The request type must specify the OUT direction (highest bit cleared). + /// The setup packet device request. + /// The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF). + /// The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF). + public void ControlOut(byte requestType, byte request, int value, int index) + { + CheckOut(requestType); + // TODO: null instead of empty buffer. But overlapped code would have to be fixed for this (no buffer to pin) + ControlTransfer(requestType, request, value, index, new byte[0]); + } + + + + /// + /// Initiates an asynchronous control transfer without a data stage over the default control endpoint. This method allows both IN and OUT direction transfers, depending + /// on the highest bit of the parameter. Alternatively, and + /// can be used for asynchronous control transfers in a specific direction, which is + /// the recommended way because it prevents using the wrong direction accidentally. Use the BeginControlTransfer method when the direction is not + /// known at compile time. + /// The setup packet request type. + /// The setup packet device request. + /// The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF). + /// The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF). + /// An optional asynchronous callback, to be called when the control transfer is complete. Can be null if no callback is required. + /// A user-provided object that distinguishes this particular asynchronous operation. Can be null if not required. + /// An object repesenting the asynchronous control transfer, which could still be pending. + /// This method always completes immediately even if the operation is still pending. The object returned represents the operation + /// and must be passed to to retrieve the result of the operation. For every call to this method a matching call to + /// must be made. When specifies a callback function, this function will be called when the operation is completed. The optional + /// parameter can be used to pass user-defined information to this callback or the . The + /// also provides an event handle () that will be triggered when the operation is complete as well. + /// + public IAsyncResult BeginControlTransfer(byte requestType, byte request, int value, int index, AsyncCallback userCallback, object stateObject) + { + // TODO: null instead of empty buffer. But overlapped code would have to be fixed for this (no buffer to pin) + return BeginControlTransfer(requestType, request, value, index, new byte[0], 0, userCallback, stateObject); + } + + + + /// + /// Initiates an asynchronous control transfer over the default control endpoint. The request should have an IN direction (specified by the highest bit + /// of the parameter). + /// The setup packet request type. The request type must specify the IN direction (highest bit set). + /// The setup packet device request. + /// The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF). + /// The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF). + /// The buffer that will receive the data transfered. + /// Length of the data to transfer. Must be equal to or less than the length of . The setup packet's length member will be set to this length. + /// An optional asynchronous callback, to be called when the control transfer is complete. Can be null if no callback is required. + /// A user-provided object that distinguishes this particular asynchronous operation. Can be null if not required. + /// An object repesenting the asynchronous control transfer, which could still be pending. + /// This method always completes immediately even if the operation is still pending. The object returned represents the operation + /// and must be passed to to retrieve the result of the operation. For every call to this method a matching call to + /// must be made. When specifies a callback function, this function will be called when the operation is completed. The optional + /// parameter can be used to pass user-defined information to this callback or the . The + /// also provides an event handle () that will be triggered when the operation is complete as well. + /// + public IAsyncResult BeginControlIn(byte requestType, byte request, int value, int index, byte[] buffer, int length, AsyncCallback userCallback, object stateObject) + { + CheckIn(requestType); + return BeginControlTransfer(requestType, request, value, index, buffer, length, userCallback, stateObject); + } + + /// + /// Initiates an asynchronous control transfer over the default control endpoint. The request should have an IN direction (specified by the highest bit + /// of the parameter). + /// The setup packet request type. The request type must specify the IN direction (highest bit set). + /// The setup packet device request. + /// The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF). + /// The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF). + /// The buffer that will receive the data transfered. The setup packet's length member will be set to the length of this buffer. + /// An optional asynchronous callback, to be called when the control transfer is complete. Can be null if no callback is required. + /// A user-provided object that distinguishes this particular asynchronous operation. Can be null if not required. + /// An object repesenting the asynchronous control transfer, which could still be pending. + /// This method always completes immediately even if the operation is still pending. The object returned represents the operation + /// and must be passed to to retrieve the result of the operation. For every call to this method a matching call to + /// must be made. When specifies a callback function, this function will be called when the operation is completed. The optional + /// parameter can be used to pass user-defined information to this callback or the . The + /// also provides an event handle () that will be triggered when the operation is complete as well. + /// + public IAsyncResult BeginControlIn(byte requestType, byte request, int value, int index, byte[] buffer, AsyncCallback userCallback, object stateObject) + { + CheckIn(requestType); + return BeginControlTransfer(requestType, request, value, index, buffer, userCallback, stateObject); + } + + /// + /// Initiates an asynchronous control transfer without a data stage over the default control endpoint. + /// The request should have an IN direction (specified by the highest bit of the parameter). + /// The setup packets' length member will be set to zero. + /// The setup packet request type. The request type must specify the IN direction (highest bit set). + /// The setup packet device request. + /// The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF). + /// The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF). + /// An optional asynchronous callback, to be called when the control transfer is complete. Can be null if no callback is required. + /// A user-provided object that distinguishes this particular asynchronous operation. Can be null if not required. + /// An object repesenting the asynchronous control transfer, which could still be pending. + /// This method always completes immediately even if the operation is still pending. The object returned represents the operation + /// and must be passed to to retrieve the result of the operation. For every call to this method a matching call to + /// must be made. When specifies a callback function, this function will be called when the operation is completed. The optional + /// parameter can be used to pass user-defined information to this callback or the . The + /// also provides an event handle () that will be triggered when the operation is complete as well. + /// + public IAsyncResult BeginControlIn(byte requestType, byte request, int value, int index, AsyncCallback userCallback, object stateObject) + { + CheckIn(requestType); + return BeginControlTransfer(requestType, request, value, index, userCallback, stateObject); + } + + /// + /// Initiates an asynchronous control transfer over the default control endpoint. The request should have an OUT direction (specified by the highest bit + /// of the parameter). + /// The setup packet request type. The request type must specify the OUT direction (highest bit cleared). + /// The setup packet device request. + /// The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF). + /// The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF). + /// The buffer that contains the data to be transfered. + /// Length of the data to transfer. Must be equal to or less than the length of . The setup packet's length member will be set to this length. + /// An optional asynchronous callback, to be called when the control transfer is complete. Can be null if no callback is required. + /// A user-provided object that distinguishes this particular asynchronous operation. Can be null if not required. + /// An object repesenting the asynchronous control transfer, which could still be pending. + /// This method always completes immediately even if the operation is still pending. The object returned represents the operation + /// and must be passed to to retrieve the result of the operation. For every call to this method a matching call to + /// must be made. When specifies a callback function, this function will be called when the operation is completed. The optional + /// parameter can be used to pass user-defined information to this callback or the . The + /// also provides an event handle () that will be triggered when the operation is complete as well. + /// + public IAsyncResult BeginControlOut(byte requestType, byte request, int value, int index, byte[] buffer, int length, AsyncCallback userCallback, object stateObject) + { + CheckOut(requestType); + return BeginControlTransfer(requestType, request, value, index, buffer, length, userCallback, stateObject); + } + + /// + /// Initiates an asynchronous control transfer over the default control endpoint. The request should have an OUT direction (specified by the highest bit + /// of the parameter). + /// The setup packet request type. The request type must specify the OUT direction (highest bit cleared). + /// The setup packet device request. + /// The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF). + /// The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF). + /// The buffer that contains the data to be transfered. The setup packet's length member will be set to the length of this buffer. + /// An optional asynchronous callback, to be called when the control transfer is complete. Can be null if no callback is required. + /// A user-provided object that distinguishes this particular asynchronous operation. Can be null if not required. + /// An object repesenting the asynchronous control transfer, which could still be pending. + /// This method always completes immediately even if the operation is still pending. The object returned represents the operation + /// and must be passed to to retrieve the result of the operation. For every call to this method a matching call to + /// must be made. When specifies a callback function, this function will be called when the operation is completed. The optional + /// parameter can be used to pass user-defined information to this callback or the . The + /// also provides an event handle () that will be triggered when the operation is complete as well. + /// + public IAsyncResult BeginControlOut(byte requestType, byte request, int value, int index, byte[] buffer, AsyncCallback userCallback, object stateObject) + { + CheckOut(requestType); + return BeginControlTransfer(requestType, request, value, index, buffer, userCallback, stateObject); + } + + /// + /// Initiates an asynchronous control transfer without a data stage over the default control endpoint. + /// The request should have an OUT direction (specified by the highest bit of the parameter). + /// The setup packets' length member will be set to zero. + /// The setup packet request type. The request type must specify the OUT direction (highest bit cleared). + /// The setup packet device request. + /// The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF). + /// The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF). + /// An optional asynchronous callback, to be called when the control transfer is complete. Can be null if no callback is required. + /// A user-provided object that distinguishes this particular asynchronous operation. Can be null if not required. + /// An object repesenting the asynchronous control transfer, which could still be pending. + /// This method always completes immediately even if the operation is still pending. The object returned represents the operation + /// and must be passed to to retrieve the result of the operation. For every call to this method a matching call to + /// must be made. When specifies a callback function, this function will be called when the operation is completed. The optional + /// parameter can be used to pass user-defined information to this callback or the . The + /// also provides an event handle () that will be triggered when the operation is complete as well. + /// + public IAsyncResult BeginControlOut(byte requestType, byte request, int value, int index, AsyncCallback userCallback, object stateObject) + { + CheckOut(requestType); + // TODO: null instead of empty buffer. But overlapped code would have to be fixed for this (no buffer to pin) + return BeginControlTransfer(requestType, request, value, index, new byte[0], userCallback, stateObject); + } + + + private void CheckNotDisposed() + { + if (_disposed) + throw new ObjectDisposedException("USB device object has been disposed."); + } + + /// + /// Finds USB devices + /// + public static USBDeviceInfo[] GetDevices(UInt16 vid, UInt16 pid) + { + API.DeviceDetails[] detailList = API.DeviceManagement.FindDevices(vid, pid); + + USBDeviceInfo[] devices = new USBDeviceInfo[detailList.Length]; + + for (int i = 0; i < detailList.Length; i++) + { + devices[i] = new USBDeviceInfo(detailList[i]); + } + return devices; + } + + /// + /// Finds the first WinUSB device with a VIA and PID + /// + public static USBDevice GetSingleDevice(UInt16 vid, UInt16 pid) + { + API.DeviceDetails[] detailList = API.DeviceManagement.FindDevices(vid, pid); + if (detailList.Length == 0) + return null; + + + return new USBDevice(detailList[0].DevicePath); + } + + + private static USBDeviceDescriptor GetDeviceDescriptor(string devicePath) + { + try + { + USBDeviceDescriptor descriptor; + using (API.WinUSBDevice wuDevice = new API.WinUSBDevice()) + { + wuDevice.OpenDevice(devicePath); + API.USB_DEVICE_DESCRIPTOR deviceDesc = wuDevice.GetDeviceDescriptor(); + + // Get first supported language ID + ushort[] langIDs = wuDevice.GetSupportedLanguageIDs(); + ushort langID = 0; + if (langIDs.Length > 0) + langID = langIDs[0]; + + string manufacturer = null, product = null, serialNumber = null; + byte idx = 0; + idx = deviceDesc.iManufacturer; + if (idx > 0) + manufacturer = wuDevice.GetStringDescriptor(idx, langID); + + idx = deviceDesc.iProduct; + if (idx > 0) + product = wuDevice.GetStringDescriptor(idx, langID); + + idx = deviceDesc.iSerialNumber; + if (idx > 0) + serialNumber = wuDevice.GetStringDescriptor(idx, langID); + descriptor = new USBDeviceDescriptor(devicePath, deviceDesc, manufacturer, product, serialNumber); + } + return descriptor; + } + catch (API.APIException e) + { + throw new USBException("Failed to retrieve device descriptor.", e); + } + } + + } +} diff --git a/FelLib/WinUSBNet/USBDeviceDescriptor.cs b/FelLib/WinUSBNet/USBDeviceDescriptor.cs new file mode 100644 index 00000000..329e6fc4 --- /dev/null +++ b/FelLib/WinUSBNet/USBDeviceDescriptor.cs @@ -0,0 +1,135 @@ +/* WinUSBNet library + * (C) 2010 Thomas Bleeker (www.madwizard.org) + * + * Licensed under the MIT license, see license.txt or: + * http://www.opensource.org/licenses/mit-license.php + */ + +using System; +using System.Collections.Generic; +using System.Text; + +namespace MadWizard.WinUSBNet +{ + /// + /// USB device details + /// + public class USBDeviceDescriptor + { + /// + /// Windows path name for the USB device + /// + public string PathName { get; private set; } + + /// + /// USB vendor ID (VID) of the device + /// + public int VID { get; private set; } + + /// + /// USB product ID (PID) of the device + /// + public int PID { get; private set; } + + /// + /// Manufacturer name, or null if not available + /// + public string Manufacturer { get; private set; } + + /// + /// Product name, or null if not available + /// + public string Product { get; private set; } + + /// + /// Device serial number, or null if not available + /// + public string SerialNumber { get; private set; } + + + /// + /// Friendly device name, or path name when no + /// further device information is available + /// + public string FullName + { + get + { + if (Manufacturer != null && Product != null) + return Product + " - " + Manufacturer; + else if (Product != null) + return Product; + else if (SerialNumber != null) + return SerialNumber; + else + return PathName; + } + } + + /// + /// Device class code as defined in the interface descriptor + /// This property can be used if the class type is not defined + /// int the USBBaseClass enumeraiton + /// + public byte ClassValue + { + get; + private set; + } + + /// + /// Device subclass code + /// + public byte SubClass + { + get; + private set; + } + + /// + /// Device protocol code + /// + public byte Protocol + { + get; + private set; + } + + /// + /// Device class code. If the device class does + /// not match any of the USBBaseClass enumeration values + /// the value will be USBBaseClass.Unknown + /// + public USBBaseClass BaseClass + { + get; + private set; + } + + internal USBDeviceDescriptor(string path, API.USB_DEVICE_DESCRIPTOR deviceDesc, string manufacturer, string product, string serialNumber) + { + PathName = path; + VID = deviceDesc.idVendor; + PID = deviceDesc.idProduct; + Manufacturer = manufacturer; + Product = product; + SerialNumber = serialNumber; + + + ClassValue = deviceDesc.bDeviceClass; + SubClass = deviceDesc.bDeviceSubClass; + Protocol = deviceDesc.bDeviceProtocol; + + // If interface class is of a known type (USBBaseeClass enum), use this + // for the InterfaceClass property. + BaseClass = USBBaseClass.Unknown; + if (Enum.IsDefined(typeof(USBBaseClass), (int)deviceDesc.bDeviceClass)) + { + BaseClass = (USBBaseClass)(int)deviceDesc.bDeviceClass; + } + + + + } + } +} diff --git a/FelLib/WinUSBNet/USBDeviceInfo.cs b/FelLib/WinUSBNet/USBDeviceInfo.cs new file mode 100644 index 00000000..4e10455c --- /dev/null +++ b/FelLib/WinUSBNet/USBDeviceInfo.cs @@ -0,0 +1,85 @@ +/* WinUSBNet library + * (C) 2010 Thomas Bleeker (www.madwizard.org) + * + * Licensed under the MIT license, see license.txt or: + * http://www.opensource.org/licenses/mit-license.php + */ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MadWizard.WinUSBNet +{ + /// + /// Gives information about a device. This information is retrieved using the setup API, not the + /// actual device descriptor. Device description and manufacturer will be the strings specified + /// in the .inf file. After a device is opened the actual device descriptor can be read as well. + /// + public class USBDeviceInfo + { + private API.DeviceDetails _details; + + /// + /// Vendor ID (VID) of the USB device + /// + public int VID + { + get + { + return _details.VID; + } + } + + /// + /// Product ID (VID) of the USB device + /// + public int PID + { + get + { + return _details.PID; + } + } + + /// + /// Manufacturer of the device, as specified in the INF file (not the device descriptor) + /// + public string Manufacturer + { + get + { + return _details.Manufacturer; + } + } + + /// + /// Description of the device, as specified in the INF file (not the device descriptor) + /// + public string DeviceDescription + { + get + { + return _details.DeviceDescription; + } + } + + /// + /// Device pathname + /// + public string DevicePath + { + get + { + return _details.DevicePath; + } + } + + internal USBDeviceInfo(API.DeviceDetails details) + { + _details = details; + } + + } +} diff --git a/FelLib/WinUSBNet/USBException.cs b/FelLib/WinUSBNet/USBException.cs new file mode 100644 index 00000000..d61d45e9 --- /dev/null +++ b/FelLib/WinUSBNet/USBException.cs @@ -0,0 +1,40 @@ +/* WinUSBNet library + * (C) 2010 Thomas Bleeker (www.madwizard.org) + * + * Licensed under the MIT license, see license.txt or: + * http://www.opensource.org/licenses/mit-license.php + */ + +using System; +using System.Collections.Generic; +using System.Text; + +namespace MadWizard.WinUSBNet +{ + /// + /// Exception used by WinUSBNet to indicate errors. This is the + /// main exception to catch when using the library. + /// + public class USBException : Exception + { + /// + /// Constructs a new USBException with the given message + /// + /// The message describing the exception + public USBException(string message) + : base(message) + { + } + + /// + /// Constructs a new USBException with the given message and underlying exception + /// that caused the USBException. + /// + /// The message describing the exception + /// The underlying exception causing the USBException + public USBException(string message, Exception innerException) + : base(message, innerException) + { + } + } +} diff --git a/FelLib/WinUSBNet/USBInterface.cs b/FelLib/WinUSBNet/USBInterface.cs new file mode 100644 index 00000000..1b13a446 --- /dev/null +++ b/FelLib/WinUSBNet/USBInterface.cs @@ -0,0 +1,152 @@ +/* WinUSBNet library + * (C) 2010 Thomas Bleeker (www.madwizard.org) + * + * Licensed under the MIT license, see license.txt or: + * http://www.opensource.org/licenses/mit-license.php + */ + +using System; +using System.Collections.Generic; +using System.Text; + +namespace MadWizard.WinUSBNet +{ + /// + /// Represents a single USB interface from a USB device + /// + public class USBInterface + { + /// + /// Collection of pipes associated with this interface + /// + public USBPipeCollection Pipes + { + get; + private set; + } + + /// + /// Interface number from the interface descriptor + /// + public int Number + { + get; + private set; + } + + /// + /// USB device associated with this interface + /// + public USBDevice Device + { + get; + private set; + } + + /// + /// First IN direction pipe on this interface + /// + public USBPipe InPipe + { + get; + private set; + } + + /// + /// First OUT direction pipe on this interface + /// + public USBPipe OutPipe + { + get; + private set; + } + + /// + /// Interface class code. If the interface class does + /// not match any of the USBBaseClass enumeration values + /// the value will be USBBaseClass.Unknown + /// + public USBBaseClass BaseClass + { + get; + private set; + } + + /// + /// Interface class code as defined in the interface descriptor + /// This property can be used if the class type is not defined + /// int the USBBaseClass enumeraiton + /// + public byte ClassValue + { + get; + private set; + } + + /// + /// Interface subclass code + /// + public byte SubClass + { + get; + private set; + } + + /// + /// Interface protocol code + /// + public byte Protocol + { + get; + private set; + } + + /// Zero based interface index in WinUSB. + /// Note that this is not necessarily the same as the interface *number* + /// from the interface descriptor. There might be interfaces within the + /// USB device that do not use WinUSB, these are not counted for index. + internal int InterfaceIndex + { + get; + private set; + } + + internal USBInterface(USBDevice device, int interfaceIndex, API.USB_INTERFACE_DESCRIPTOR rawDesc, USBPipeCollection pipes) + { + // Set raw class identifiers + ClassValue = rawDesc.bInterfaceClass; + SubClass = rawDesc.bInterfaceSubClass; + Protocol = rawDesc.bInterfaceProtocol; + + Number = rawDesc.bInterfaceNumber; + InterfaceIndex = interfaceIndex; + + // If interface class is of a known type (USBBaseClass enum), use this + // for the InterfaceClass property. + BaseClass = USBBaseClass.Unknown; + if (Enum.IsDefined(typeof(USBBaseClass), (int)rawDesc.bInterfaceClass)) + { + BaseClass = (USBBaseClass)(int)rawDesc.bInterfaceClass; + } + + + Device = device; + Pipes = pipes; + + // Handle pipes + foreach (USBPipe pipe in pipes) + { + // Attach pipe to this interface + pipe.AttachInterface(this); + + // If first in or out pipe, set InPipe and OutPipe + if (pipe.IsIn && InPipe == null) + InPipe = pipe; + if (pipe.IsOut && OutPipe == null) + OutPipe = pipe; + + } + + } + } +} diff --git a/FelLib/WinUSBNet/USBInterfaceCollection.cs b/FelLib/WinUSBNet/USBInterfaceCollection.cs new file mode 100644 index 00000000..4f4a8ee6 --- /dev/null +++ b/FelLib/WinUSBNet/USBInterfaceCollection.cs @@ -0,0 +1,160 @@ +/* WinUSBNet library + * (C) 2010 Thomas Bleeker (www.madwizard.org) + * + * Licensed under the MIT license, see license.txt or: + * http://www.opensource.org/licenses/mit-license.php + */ + +using System; +using System.Text; +using System.Collections; +using System.Collections.Generic; + +namespace MadWizard.WinUSBNet +{ + /// + /// Collection of UsbInterface objects + /// + public class USBInterfaceCollection : IEnumerable + { + private USBInterface[] _interfaces; + + internal USBInterfaceCollection(USBInterface[] interfaces) + { + _interfaces = interfaces; + } + + private class USBInterfaceEnumerator : IEnumerator + { + private int _index; + private USBInterface[] _interfaces; + + public USBInterfaceEnumerator(USBInterface[] interfaces) + { + _interfaces = interfaces; + _index = -1; + } + + public void Dispose() + { + // Intentionally empty + } + private USBInterface GetCurrent() + { + try + { + return _interfaces[_index]; + } + + catch (IndexOutOfRangeException) + { + throw new InvalidOperationException(); + } + } + + public USBInterface Current + { + get + { + return GetCurrent(); + } + } + + + object IEnumerator.Current + { + get + { + return GetCurrent(); + } + } + + public bool MoveNext() + { + _index++; + return _index < _interfaces.Length; + } + + public void Reset() + { + _index = -1; + } + } + + /// + /// Finds the first interface with that matches the device class + /// given by the parameter. + /// + /// The device class the interface should match + /// The first interface with the given interface class, or null + /// if no such interface exists. + public USBInterface Find(USBBaseClass interfaceClass) + { + for (int i = 0; i < _interfaces.Length; i++) + { + USBInterface iface = _interfaces[i]; + if (iface.BaseClass == interfaceClass) + return iface; + } + return null; + } + + /// + /// Finds all interfaces matching the device class given by the + /// parameter. + /// + /// The device class the interface should match + /// An array of USBInterface objects matching the device class, or an empty + /// array if no interface matches. + public USBInterface[] FindAll(USBBaseClass interfaceClass) + { + List matchingInterfaces = new List(); + for (int i = 0; i < _interfaces.Length; i++) + { + USBInterface iface = _interfaces[i]; + if (iface.BaseClass == interfaceClass) + matchingInterfaces.Add(iface); + } + return matchingInterfaces.ToArray(); + } + + /// + /// Returns a typed enumerator that iterates through a collection. + /// + /// The enumerator object that can be used to iterate through the collection. + public IEnumerator GetEnumerator() + { + return new USBInterfaceEnumerator(_interfaces); + } + + /// + /// Get interface by interface number + /// + /// Number of the interface to return. Note: this is the number from the interface descriptor, which + /// is not necessarily the same as the interface index. + /// Thrown when the given interface number does not exist in the collection. + /// + public USBInterface this[ int interfaceNumber ] + { + get + { + for (int i = 0; i < _interfaces.Length; i++) + { + USBInterface iface = _interfaces[i]; + if (iface.Number == interfaceNumber) + return iface; + } + throw new IndexOutOfRangeException(string.Format("No interface with number {0} exists.", interfaceNumber)); + } + } + + /// + /// Returns an enumerator that iterates through a collection. + /// + /// An IEnumerator object that can be used to iterate through the collection. + IEnumerator IEnumerable.GetEnumerator() + { + return new USBInterfaceEnumerator(_interfaces); + } + } +} diff --git a/FelLib/WinUSBNet/USBNotifier.cs b/FelLib/WinUSBNet/USBNotifier.cs new file mode 100644 index 00000000..b97c01d9 --- /dev/null +++ b/FelLib/WinUSBNet/USBNotifier.cs @@ -0,0 +1,190 @@ +/* WinUSBNet library + * (C) 2010 Thomas Bleeker (www.madwizard.org) + * + * Licensed under the MIT license, see license.txt or: + * http://www.opensource.org/licenses/mit-license.php + */ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace MadWizard.WinUSBNet +{ + /// + /// Delegate for event handler methods handing USB events + /// + /// The source of the event + /// Details of the event + public delegate void USBEventHandler(object sender, USBEvent e); + + /// + /// Event type enumeration for WinUSB events + /// + public enum USBEventType + { + /// + /// A device has been connected to the system + /// + DeviceArrival, + + /// + /// A device has been disconnected from the system + /// + DeviceRemoval, + } + + /// + /// Contains the details of a USB event + /// + public class USBEvent : EventArgs + { + /// + /// WinUSB interface GUID of the device as specified in the WinUSBNotifier + /// + public Guid Guid; + + /// + /// Device pathname that identifies the device + /// + public string DevicePath; + + /// + /// Type of event that occurred + /// + public USBEventType Type; + + internal USBEvent(USBEventType type, Guid guid, string devicePath) + { + this.Guid = guid; + this.DevicePath = devicePath; + this.Type= type; + } + } + + /// + /// Helper class to receive notifications on USB device changes such as + /// connecting or removing a device. + /// + public class USBNotifier : IDisposable + { + private DeviceNotifyHook _hook; + private Guid _guid; + + /// + /// Event triggered when a new USB device that matches the USBNotifier's GUID is connected + /// + public event USBEventHandler Arrival; + + /// + /// Event triggered when a new USB device that matches the USBNotifier's GUID is disconnected + /// + public event USBEventHandler Removal; + + /// + /// The interface GUID of devices this USBNotifier will watch + /// + public Guid Guid + { + get + { + return _guid; + } + } + + /// + /// Constructs a new USBNotifier that will watch for events on + /// devices matching the given interface GUID. A Windows Forms control + /// is needed since the notifier relies on window messages. + /// + /// A control that will be used internally for device notification messages. + /// You can use a Form object for example. + /// The interface GUID string of the devices to watch. + public USBNotifier(Control control, string guidString) : + this(control, new Guid(guidString)) + { + // Handled in other constructor + } + + + /// + /// Constructs a new USBNotifier that will watch for events on + /// devices matching the given interface GUID. A Windows Forms control + /// is needed since the notifier relies on window messages. + /// + /// A control that will be used internally for device notification messages. + /// You can use a Form object for example. + /// The interface GUID of the devices to watch. + public USBNotifier(Control control, Guid guid) + { + _guid = guid; + _hook = new DeviceNotifyHook(this, control, _guid); + } + + /// + /// Triggers the arrival event + /// + /// Device pathname of the device that has been connected + protected void OnArrival(string devicePath) + { + if (Arrival != null) + Arrival(this, new USBEvent(USBEventType.DeviceArrival, _guid, devicePath)); + } + /// + /// Trigggers the removal event + /// + /// Device pathname of the device that has been connected + protected void OnRemoval(string devicePath) + { + if (Removal != null) + Removal(this, new USBEvent(USBEventType.DeviceRemoval, _guid, devicePath)); + } + + internal void HandleDeviceChange(Message m) + { + if (m.Msg != API.DeviceManagement.WM_DEVICECHANGE) + throw new USBException("Invalid device change message."); // should not happen + + if ((int)m.WParam == API.DeviceManagement.DBT_DEVICEARRIVAL) + { + + string devName = API.DeviceManagement.GetNotifyMessageDeviceName(m, _guid); + if (devName != null) + OnArrival(devName); + } + + if ((int)m.WParam == API.DeviceManagement.DBT_DEVICEREMOVECOMPLETE) + { + string devName = API.DeviceManagement.GetNotifyMessageDeviceName(m, _guid); + if (devName != null) + OnRemoval(devName); + } + + } + + /// + /// Disposes the USBNotifier object and frees all resources. + /// Call this method when the object is no longer needed. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Disposes the object's resources. + /// + /// True when dispose is called manually, false when called by the finalizer. + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + _hook.Dispose(); + } + } + + } +} diff --git a/FelLib/WinUSBNet/USBPipe.cs b/FelLib/WinUSBNet/USBPipe.cs new file mode 100644 index 00000000..9a565633 --- /dev/null +++ b/FelLib/WinUSBNet/USBPipe.cs @@ -0,0 +1,387 @@ +/* WinUSBNet library + * (C) 2010 Thomas Bleeker (www.madwizard.org) + * + * Licensed under the MIT license, see license.txt or: + * http://www.opensource.org/licenses/mit-license.php + */ + +using System; +using System.Collections.Generic; +using System.Text; + +namespace MadWizard.WinUSBNet +{ + /// + /// UsbPipe represents a single pipe on a WinUSB device. A pipe is connected + /// to a certain endpoint on the device and has a fixed direction (IN or OUT) + /// + public class USBPipe + { + private API.WINUSB_PIPE_INFORMATION _pipeInfo; + private USBInterface _interface = null; + private USBDevice _device; + private USBPipePolicy _policy; + + /// + /// Endpoint address including the direction in the most significant bit + /// + public byte Address + { + get + { + return _pipeInfo.PipeId; + } + } + + /// + /// The USBDevice this pipe is associated with + /// + public USBDevice Device + { + get + { + return _device; + } + } + + /// + /// Maximum packet size for transfers on this endpoint + /// + public int MaximumPacketSize + { + get + { + return _pipeInfo.MaximumPacketSize; + } + } + + /// + /// The interface associated with this pipe + /// + public USBInterface Interface + { + get + { + return _interface; + } + } + + /// + /// The pipe policy settings for this pipe + /// + public USBPipePolicy Policy + { + get + { + return _policy; + } + } + + /// + /// True if the pipe has direction OUT (host to device), false otherwise. + /// + public bool IsOut + { + get + { + return (_pipeInfo.PipeId & 0x80) == 0; + } + } + + /// + /// True if the pipe has direction IN (device to host), false otherwise. + /// + public bool IsIn + { + get + { + return (_pipeInfo.PipeId & 0x80) != 0; + } + } + + /// + /// Reads data from the pipe into a buffer. + /// + /// The buffer to read data into. The maximum number of bytes that will be read is specified by the length of the buffer. + /// The number of bytes read from the pipe. + public int Read(byte[] buffer) + { + return Read(buffer, 0, buffer.Length); + } + + /// + /// Reads data from the pipe into a buffer. + /// + /// The buffer to read data into. + /// The byte offset in from which to begin writing data read from the pipe. + /// The maximum number of bytes to read, starting at offset + /// The number of bytes read from the pipe. + public int Read(byte[] buffer, int offset, int length) + { + CheckReadParams(buffer, offset, length); + + try + { + uint bytesRead; + + _device.InternalDevice.ReadPipe(Interface.InterfaceIndex, _pipeInfo.PipeId, buffer, offset, length, out bytesRead); + + return (int)bytesRead; + } + catch (API.APIException e) + { + throw new USBException("Failed to read from pipe.", e); + } + } + + private void CheckReadParams(byte[] buffer, int offset, int length) + { + if (!IsIn) + throw new NotSupportedException("Cannot read from a pipe with OUT direction."); + + int bufferLength = buffer.Length; + if (offset < 0 || offset >= bufferLength) + throw new ArgumentOutOfRangeException("Offset of data to read is outside the buffer boundaries."); + if (length < 0 || (offset + length) > bufferLength) + throw new ArgumentOutOfRangeException("Length of data to read is outside the buffer boundaries."); + } + private void CheckWriteParams(byte[] buffer, int offset, int length) + { + if (!IsOut) + throw new NotSupportedException("Cannot write to a pipe with IN direction."); + + int bufferLength = buffer.Length; + if (offset < 0 || offset >= bufferLength) + throw new ArgumentOutOfRangeException("Offset of data to write is outside the buffer boundaries."); + if (length < 0 || (offset + length) > bufferLength) + throw new ArgumentOutOfRangeException("Length of data to write is outside the buffer boundaries."); + + } + + /// Initiates an asynchronous read operation on the pipe. + /// Buffer that will receive the data read from the pipe. + /// Byte offset within the buffer at which to begin writing the data received. + /// Length of the data to transfer. + /// An optional asynchronous callback, to be called when the operation is complete. Can be null if no callback is required. + /// A user-provided object that distinguishes this particular asynchronous operation. Can be null if not required. + /// An object repesenting the asynchronous operation, which could still be pending. + /// This method always completes immediately even if the operation is still pending. The object returned represents the operation + /// and must be passed to to retrieve the result of the operation. For every call to this method a matching call to + /// must be made. When specifies a callback function, this function will be called when the operation is completed. The optional + /// parameter can be used to pass user-defined information to this callback or the . The + /// also provides an event handle () that will be triggered when the operation is complete as well. + /// + public IAsyncResult BeginRead(byte[] buffer, int offset, int length, AsyncCallback userCallback, object stateObject) + { + CheckReadParams(buffer, offset, length); + + USBAsyncResult result = new USBAsyncResult(userCallback, stateObject); + try + { + _device.InternalDevice.ReadPipeOverlapped(Interface.InterfaceIndex, _pipeInfo.PipeId, buffer, offset, length, result); + } + catch (API.APIException e) + { + if (result != null) + result.Dispose(); + throw new USBException("Failed to read from pipe.", e); + } + catch (Exception) + { + if (result != null) + result.Dispose(); + throw; + } + return result; + } + + /// + /// Waits for a pending asynchronous read operation to complete. + /// + /// The object representing the asynchonous operation, + /// as returned by . + /// The number of bytes transfered during the operation. + /// Every call to must have a matching call to to dispose + /// of any resources used and to retrieve the result of the operation. When the operation was successful the method returns the number + /// of bytes that were transfered. If an error occurred during the operation this method will throw the exceptions that would + /// otherwise have ocurred during the operation. If the operation is not yet finished EndWrite will wait for the + /// operation to finish before returning. + public int EndRead(IAsyncResult asyncResult) + { + if (asyncResult == null) + throw new NullReferenceException("asyncResult cannot be null"); + if (!(asyncResult is USBAsyncResult)) + throw new ArgumentException("AsyncResult object was not created by calling BeginRead on this class."); + + // todo: check duplicate end reads? + USBAsyncResult result = (USBAsyncResult)asyncResult; + try + { + if (!result.IsCompleted) + result.AsyncWaitHandle.WaitOne(); + + if (result.Error != null) + throw new USBException("Asynchronous read from pipe has failed.", result.Error); + + return result.BytesTransfered; + } + finally + { + result.Dispose(); + } + + } + + /// + /// Writes data from a buffer to the pipe. + /// + /// The buffer to write data from. The complete buffer will be written to the device. + public void Write(byte[] buffer) + { + Write(buffer, 0, buffer.Length); + } + + /// + /// Writes data from a buffer to the pipe. + /// + /// The buffer to write data from. + /// The byte offset in from which to begin writing. + /// The number of bytes to write, starting at offset + public void Write(byte[] buffer, int offset, int length) + { + CheckWriteParams(buffer, offset, length); + + try + { + _device.InternalDevice.WritePipe(Interface.InterfaceIndex, _pipeInfo.PipeId, buffer, offset, length); + } + catch (API.APIException e) + { + throw new USBException("Failed to write to pipe.", e); + } + } + + /// Initiates an asynchronous write operation on the pipe. + /// Buffer that contains the data to write to the pipe. + /// Byte offset within the buffer from which to begin writing. + /// Length of the data to transfer. + /// An optional asynchronous callback, to be called when the operation is complete. Can be null if no callback is required. + /// A user-provided object that distinguishes this particular asynchronous operation. Can be null if not required. + /// An object repesenting the asynchronous operation, which could still be pending. + /// This method always completes immediately even if the operation is still pending. The object returned represents the operation + /// and must be passed to to retrieve the result of the operation. For every call to this method a matching call to + /// must be made. When specifies a callback function, this function will be called when the operation is completed. The optional + /// parameter can be used to pass user-defined information to this callback or the . The + /// also provides an event handle () that will be triggered when the operation is complete as well. + /// + public IAsyncResult BeginWrite(byte[] buffer, int offset, int length, AsyncCallback userCallback, object stateObject) + { + CheckWriteParams(buffer, offset, length); + + USBAsyncResult result = new USBAsyncResult(userCallback, stateObject); + try + { + _device.InternalDevice.WriteOverlapped(Interface.InterfaceIndex, _pipeInfo.PipeId, buffer, offset, length, result); + } + catch (API.APIException e) + { + if (result != null) + result.Dispose(); + throw new USBException("Failed to write to pipe.", e); + } + catch (Exception) + { + if (result != null) + result.Dispose(); + throw; + } + return result; + } + + /// + /// Waits for a pending asynchronous write operation to complete. + /// + /// The object representing the asynchonous operation, + /// as returned by . + /// The number of bytes transfered during the operation. + /// Every call to must have a matching call to to dispose + /// of any resources used and to retrieve the result of the operation. When the operation was successful the method returns the number + /// of bytes that were transfered. If an error occurred during the operation this method will throw the exceptions that would + /// otherwise have ocurred during the operation. If the operation is not yet finished EndWrite will wait for the + /// operation to finish before returning. + public void EndWrite(IAsyncResult asyncResult) + { + if (asyncResult == null) + throw new NullReferenceException("asyncResult cannot be null"); + if (!(asyncResult is USBAsyncResult)) + throw new ArgumentException("AsyncResult object was not created by calling BeginWrite on this class."); + + USBAsyncResult result = (USBAsyncResult)asyncResult; + try + { + // todo: check duplicate end writes? + + if (!result.IsCompleted) + result.AsyncWaitHandle.WaitOne(); + + if (result.Error != null) + throw new USBException("Asynchronous write to pipe has failed.", result.Error); + } + finally + { + result.Dispose(); + } + } + + /// + /// Aborts all pending transfers for this pipe. + /// + public void Abort() + { + try + { + _device.InternalDevice.AbortPipe(Interface.InterfaceIndex, _pipeInfo.PipeId); + } + catch (API.APIException e) + { + throw new USBException("Failed to abort pipe.", e); + } + } + + /// + /// Flushes the pipe, discarding any data that is cached. Only available on IN direction pipes. + /// + public void Flush() + { + if (!IsIn) + throw new NotSupportedException("Flush is only supported on IN direction pipes"); + try + { + _device.InternalDevice.FlushPipe(Interface.InterfaceIndex, _pipeInfo.PipeId); + } + catch (API.APIException e) + { + throw new USBException("Failed to flush pipe.", e); + } + } + + internal USBPipe(USBDevice device, API.WINUSB_PIPE_INFORMATION pipeInfo) + { + _pipeInfo = pipeInfo; + _device = device; + + // Policy is not set until interface is attached + _policy = null; + } + + internal void AttachInterface(USBInterface usbInterface) + { + _interface = usbInterface; + + // Initialize policy now that interface is set (policy requires interface) + _policy = new USBPipePolicy(_device, _interface.InterfaceIndex, _pipeInfo.PipeId); + } + + } + +} \ No newline at end of file diff --git a/FelLib/WinUSBNet/USBPipeCollection.cs b/FelLib/WinUSBNet/USBPipeCollection.cs new file mode 100644 index 00000000..a91d8d58 --- /dev/null +++ b/FelLib/WinUSBNet/USBPipeCollection.cs @@ -0,0 +1,135 @@ +/* WinUSBNet library + * (C) 2010 Thomas Bleeker (www.madwizard.org) + * + * Licensed under the MIT license, see license.txt or: + * http://www.opensource.org/licenses/mit-license.php + */ + +using System; +using System.Text; +using System.Collections; +using System.Collections.Generic; + +namespace MadWizard.WinUSBNet +{ + /// + /// Collection of UsbPipe objects + /// + public class USBPipeCollection : IEnumerable + { + private Dictionary _pipes; + + internal USBPipeCollection(USBPipe[] pipes) + { + _pipes = new Dictionary(pipes.Length); + foreach (USBPipe pipe in pipes) + { + if (_pipes.ContainsKey(pipe.Address)) + throw new USBException("Duplicate pipe address in endpoint."); + _pipes[pipe.Address] = pipe; + } + } + + /// + /// Returns the pipe from the collection with the given pipe address + /// + /// Address of the pipe to return + /// The pipe with the given pipe address + /// Thrown if no pipe with the specified address + /// is available in the collection. + public USBPipe this [byte pipeAddress] + { + get + { + USBPipe pipe; + if (!_pipes.TryGetValue(pipeAddress, out pipe)) + throw new IndexOutOfRangeException(); + return pipe; + } + } + + private class UsbPipeEnumerator : IEnumerator + { + private int _index; + private USBPipe[] _pipes; + + public UsbPipeEnumerator(USBPipe[] pipes) + { + _pipes = pipes; + _index = -1; + } + + public void Dispose() + { + // Empty + } + private USBPipe GetCurrent() + { + try + { + return _pipes[_index]; + } + + catch (IndexOutOfRangeException) + { + throw new InvalidOperationException(); + } + } + + public USBPipe Current + { + get + { + return GetCurrent(); + } + } + + + object IEnumerator.Current + { + get + { + return GetCurrent(); + } + } + + public bool MoveNext() + { + _index++; + return _index < _pipes.Length; + } + + public void Reset() + { + _index = -1; + } + + } + + private USBPipe[] GetPipeList() + { + var values = _pipes.Values; + USBPipe[] pipeList = new USBPipe[values.Count]; + values.CopyTo(pipeList, 0); + return pipeList; + } + + /// + /// Returns a typed enumerator that iterates through a collection. + /// + /// The enumerator object that can be used to iterate through the collection. + public IEnumerator GetEnumerator() + { + return new UsbPipeEnumerator(GetPipeList()); + } + + /// + /// Returns an enumerator that iterates through a collection. + /// + /// An IEnumerator object that can be used to iterate through the collection. + IEnumerator IEnumerable.GetEnumerator() + { + return new UsbPipeEnumerator(GetPipeList()); + } + } +} diff --git a/FelLib/WinUSBNet/USBPipePolicy.cs b/FelLib/WinUSBNet/USBPipePolicy.cs new file mode 100644 index 00000000..800b748e --- /dev/null +++ b/FelLib/WinUSBNet/USBPipePolicy.cs @@ -0,0 +1,181 @@ +/* WinUSBNet library + * (C) 2010 Thomas Bleeker (www.madwizard.org) + * + * Licensed under the MIT license, see license.txt or: + * http://www.opensource.org/licenses/mit-license.php + */ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MadWizard.WinUSBNet +{ + + /// + /// Describes the policy for a specific USB pipe + /// + public class USBPipePolicy + { + + private byte _pipeID; + private int _interfaceIndex; + private USBDevice _device; + + internal USBPipePolicy(USBDevice device, int interfaceIndex, byte pipeID) + { + _pipeID = pipeID; + _interfaceIndex = interfaceIndex; + _device = device; + } + + + + private void RequireDirectionOut() + { + // Some policy types only apply specifically to OUT direction pipes + if ((_pipeID & 0x80) != 0) + throw new NotSupportedException("This policy type is only allowed on OUT direction pipes."); + } + + private void RequireDirectionIn() + { + // Some policy types only apply specifically to IN direction pipes + // This function checks for this. + if ((_pipeID & 0x80) == 0) + throw new NotSupportedException("This policy type is only allowed on IN direction pipes."); + } + + /// + /// When false, read requests fail when the device returns more data than requested. When true, extra data is + /// saved and returned on the next read. Default value is true. Only available on IN direction pipes. + /// + /// WinUSB_GetPipePolicy for a more detailed description + public bool AllowPartialReads + { + get + { + RequireDirectionIn(); + return _device.InternalDevice.GetPipePolicyBool(_interfaceIndex, _pipeID, API.POLICY_TYPE.ALLOW_PARTIAL_READS); + } + set + { + RequireDirectionIn(); + _device.InternalDevice.SetPipePolicy(_interfaceIndex, _pipeID, API.POLICY_TYPE.ALLOW_PARTIAL_READS, value); + } + } + + /// + /// When true, the driver fails stalled data transfers, but the driver clears the stall condition automatically. Default + /// value is false. + /// + /// WinUSB_GetPipePolicy for a more detailed description + public bool AutoClearStall + { + get + { + return _device.InternalDevice.GetPipePolicyBool(_interfaceIndex, _pipeID, API.POLICY_TYPE.AUTO_CLEAR_STALL); + } + set + { + _device.InternalDevice.SetPipePolicy(_interfaceIndex, _pipeID, API.POLICY_TYPE.AUTO_CLEAR_STALL, value); + } + } + + /// + /// If both AllowPartialReads and AutoFlush are true, when the device returns more data than requested by the client it + /// will discard the remaining data. Default value is false. Only available on IN direction pipes. + /// + /// WinUSB_GetPipePolicy for a more detailed description + public bool AutoFlush + { + get + { + RequireDirectionIn(); + return _device.InternalDevice.GetPipePolicyBool(_interfaceIndex, _pipeID, API.POLICY_TYPE.AUTO_FLUSH); ; + } + set + { + RequireDirectionIn(); + _device.InternalDevice.SetPipePolicy(_interfaceIndex, _pipeID, API.POLICY_TYPE.AUTO_FLUSH, value); + } + } + /// + /// When true, read operations are completed only when the number of bytes requested by the client has been received. Default value is false. + /// Only available on IN direction pipes. + /// + /// WinUSB_GetPipePolicy for a more detailed description + public bool IgnoreShortPackets + { + get + { + RequireDirectionIn(); + return _device.InternalDevice.GetPipePolicyBool(_interfaceIndex, _pipeID, API.POLICY_TYPE.IGNORE_SHORT_PACKETS); ; + } + set + { + RequireDirectionIn(); + _device.InternalDevice.SetPipePolicy(_interfaceIndex, _pipeID, API.POLICY_TYPE.IGNORE_SHORT_PACKETS, value); + } + } + + /// + /// Specifies the timeout in milliseconds for pipe operations. If an operation does not finish within the specified time it will fail. + /// When set to zero, no timeout is used. Default value is zero. + /// + /// WinUSB_GetPipePolicy for a more detailed description + public int PipeTransferTimeout + { + get + { + return (int)_device.InternalDevice.GetPipePolicyUInt(_interfaceIndex, _pipeID, API.POLICY_TYPE.PIPE_TRANSFER_TIMEOUT); + } + set + { + if (value < 0) + throw new ArgumentOutOfRangeException("Pipe transfer timeout cannot be negative."); + _device.InternalDevice.SetPipePolicy(_interfaceIndex, _pipeID, API.POLICY_TYPE.PIPE_TRANSFER_TIMEOUT, (uint)value); + } + } + + /// + /// When true, read and write operations to the pipe must have a buffer length that is a multiple of the maximum endpoint packet size, + /// and the length must be less than the maximum transfer size. With these conditions met, data is sent directly to the USB driver stack, + /// bypassing the queuing and error handling of WinUSB. + /// Default value is false. + /// + /// WinUSB_GetPipePolicy for a more detailed description + public bool RawIO + { + get + { + return _device.InternalDevice.GetPipePolicyBool(_interfaceIndex, _pipeID, API.POLICY_TYPE.RAW_IO); ; + } + set + { + _device.InternalDevice.SetPipePolicy(_interfaceIndex, _pipeID, API.POLICY_TYPE.RAW_IO, value); + } + } + + /// + /// When true, every write request that is a multiple of the maximum packet size for the endpoint is terminated with a zero-length packet. + /// Default value is false. Only available on OUT direction pipes. + /// + /// WinUSB_GetPipePolicy for a more detailed description + public bool ShortPacketTerminate + { + get + { + RequireDirectionOut(); + return _device.InternalDevice.GetPipePolicyBool(_interfaceIndex, _pipeID, API.POLICY_TYPE.SHORT_PACKET_TERMINATE); ; + } + set + { + RequireDirectionOut(); + _device.InternalDevice.SetPipePolicy(_interfaceIndex, _pipeID, API.POLICY_TYPE.SHORT_PACKET_TERMINATE, value); + } + } + + } +} -- cgit v1.2.3