Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/ClusterM/hakchi2.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/FelLib
diff options
context:
space:
mode:
authorAlexey 'Cluster' Avdyukhin <clusterrr@clusterrr.com>2017-01-07 19:18:29 +0300
committerAlexey 'Cluster' Avdyukhin <clusterrr@clusterrr.com>2017-01-07 19:18:29 +0300
commitbaebd293c5e8cc41ebcd11464868e73c0a8044db (patch)
treeee1ec4f7d85863099b3d1d7656935295973e352d /FelLib
First commit
Diffstat (limited to 'FelLib')
-rw-r--r--FelLib/AWFELMessage.cs55
-rw-r--r--FelLib/AWFELStandardRequest.cs47
-rw-r--r--FelLib/AWFELStatusResponse.cs39
-rw-r--r--FelLib/AWFELVerifyDeviceResponse.cs67
-rw-r--r--FelLib/AWUSBRequest.cs60
-rw-r--r--FelLib/AWUSBResponse.cs50
-rw-r--r--FelLib/Fel.cs200
-rw-r--r--FelLib/FelException.cs14
-rw-r--r--FelLib/FelParseException.cs11
-rw-r--r--FelLib/WinUSBNet/API/APIException.cs45
-rw-r--r--FelLib/WinUSBNet/API/DeviceDetails.cs23
-rw-r--r--FelLib/WinUSBNet/API/DeviceManagement.cs311
-rw-r--r--FelLib/WinUSBNet/API/DeviceManagementAPI.cs159
-rw-r--r--FelLib/WinUSBNet/API/FileAPI.cs39
-rw-r--r--FelLib/WinUSBNet/API/WinUSBDevice.cs477
-rw-r--r--FelLib/WinUSBNet/API/WinUSBDeviceAPI.cs202
-rw-r--r--FelLib/WinUSBNet/DeviceNotifyHook.cs117
-rw-r--r--FelLib/WinUSBNet/USB.cs80
-rw-r--r--FelLib/WinUSBNet/USBAsyncResult.cs136
-rw-r--r--FelLib/WinUSBNet/USBDevice.cs756
-rw-r--r--FelLib/WinUSBNet/USBDeviceDescriptor.cs135
-rw-r--r--FelLib/WinUSBNet/USBDeviceInfo.cs85
-rw-r--r--FelLib/WinUSBNet/USBException.cs40
-rw-r--r--FelLib/WinUSBNet/USBInterface.cs152
-rw-r--r--FelLib/WinUSBNet/USBInterfaceCollection.cs160
-rw-r--r--FelLib/WinUSBNet/USBNotifier.cs190
-rw-r--r--FelLib/WinUSBNet/USBPipe.cs387
-rw-r--r--FelLib/WinUSBNet/USBPipeCollection.cs135
-rw-r--r--FelLib/WinUSBNet/USBPipePolicy.cs181
29 files changed, 4353 insertions, 0 deletions
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<byte>();
+ 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<byte>();
+ 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
+{
+ /// <summary>
+ /// Exception used internally to catch Win32 API errors. This exception should
+ /// not be thrown to the library's caller.
+ /// </summary>
+ 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
+{
+ /// <summary>
+ /// Routines for detecting devices and receiving device notifications.
+ /// </summary>
+ 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<DeviceDetails> deviceList = new List<DeviceDetails>();
+ 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
+{
+ /// <summary>
+ /// API declarations relating to device management (SetupDixxx and
+ /// RegisterDeviceNotification functions).
+ /// </summary>
+
+ 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
+{
+ /// <summary>
+ /// API declarations relating to file I/O (and used by WinUsb).
+ /// </summary>
+
+ 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
+{
+ /// <summary>
+ /// Wrapper for a WinUSB device dealing with the WinUSB and additional interface handles
+ /// </summary>
+ 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<WINUSB_PIPE_INFORMATION>();
+ 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<IntPtr> interfaces = new List<IntPtr>();
+ 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
+{
+ /// <summary>
+ /// USB base class code enumeration, as defined in the USB specification
+ /// </summary>
+ public enum USBBaseClass
+ {
+ /// <summary>Unknown non-zero class code. Used when the actual class code
+ /// does not match any of the ones defined in this enumeration. </summary>
+ Unknown = -1,
+
+ /// <summary>Base class defined elsewhere (0x00)</summary>
+ None = 0x00,
+
+ /// <summary>Audio base class (0x01)</summary>
+ Audio = 0x01,
+
+ /// <summary>Communications and CDC control base class (0x02)</summary>
+ CommCDC = 0x02,
+
+ /// <summary>HID base class (0x03)</summary>
+ HID = 0x03,
+
+ /// <summary>Physical base class (0x05)</summary>
+ Physical = 0x05,
+
+ /// <summary>Image base class (0x06)</summary>
+ Image = 0x06,
+
+ /// <summary>Printer base class (0x07)</summary>
+ Printer = 0x07,
+
+ /// <summary>Mass storage base class (0x08)</summary>
+ MassStorage = 0x08,
+
+ /// <summary>Hub base class (0x09)</summary>
+ Hub = 0x09,
+
+ /// <summary>CDC data base class (0x0A)</summary>
+ CDCData = 0x0A,
+
+ /// <summary>Smart card base class (0x0B)</summary>
+ SmartCard = 0x0B,
+
+ /// <summary>Content security base class (0x0D)</summary>
+ ContentSecurity = 0x0D,
+
+ /// <summary>Video base class (0x0E)</summary>
+ Video = 0x0E,
+
+ /// <summary>Personal healthcare base class (0x0F)</summary>
+ PersonalHealthcare = 0x0F,
+
+ /// <summary>Diagnosticdevice base class (0xDC)</summary>
+ DiagnosticDevice = 0xDC,
+
+ /// <summary>Wireless controller base class (0xE0)</summary>
+ WirelessController = 0xE0,
+
+ /// <summary>Miscellaneous base class (0xEF)</summary>
+ Miscellaneous = 0xEF,
+
+ /// <summary>Application specific base class (0xFE)</summary>
+ ApplicationSpecific = 0xFE,
+
+ /// <summary>Vendor specific base class (0xFF)</summary>
+ 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
+{
+ /// <summary>
+ /// The UsbDevice class represents a single WinUSB device.
+ /// </summary>
+ public class USBDevice : IDisposable
+ {
+ private API.WinUSBDevice _wuDevice = null;
+ private bool _disposed = false;
+
+ /// <summary>
+ /// Collection of all pipes available on the USB device
+ /// </summary>
+ public USBPipeCollection Pipes
+ {
+ get;
+ private set;
+ }
+
+
+
+
+ /// <summary>
+ /// Collection of all interfaces available on the USB device
+ /// </summary>
+ public USBInterfaceCollection Interfaces
+ {
+ get;
+ private set;
+ }
+
+ /// <summary>
+ /// Device descriptor with information about the device
+ /// </summary>
+ public USBDeviceDescriptor Descriptor
+ {
+ get;
+ private set;
+ }
+
+ /// <summary>
+ /// Constructs a new USB device
+ /// </summary>
+ /// <param name="deviceInfo">USB device info of the device to create</param>
+ public USBDevice(USBDeviceInfo deviceInfo)
+ : this(deviceInfo.DevicePath)
+ {
+ // Handled in other constructor
+ }
+
+ /// <summary>
+ /// 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.
+ /// </summary>
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ /// <summary>
+ /// Finalizer for the UsbDevice. Disposes all unmanaged handles.
+ /// </summary>
+ ~USBDevice()
+ {
+ Dispose(false);
+ }
+
+ /// <summary>
+ /// Disposes the object
+ /// </summary>
+ /// <param name="disposing">Indicates wether Dispose was called manually (true) or by
+ /// the garbage collector (false) via the destructor.</param>
+ protected virtual void Dispose(bool disposing)
+ {
+ if (_disposed)
+ return;
+
+ if (disposing)
+ {
+ if (_wuDevice != null)
+ _wuDevice.Dispose();
+ }
+
+ // Clean unmanaged resources here.
+ // (none currently)
+
+ _disposed = true;
+ }
+
+ /// <summary>
+ /// Constructs a new USB device
+ /// </summary>
+ /// <param name="devicePathName">Device path name of the USB device to create</param>
+ 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<USBPipe> allPipes = new List<USBPipe>();
+
+ 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");
+ }
+
+ /// <summary>
+ /// 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.
+ /// </summary>
+ /// <seealso href="http://msdn.microsoft.com/en-us/library/aa476439.aspx">WinUSB_GetPipePolicy for a more detailed description</seealso>
+ 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);
+ }
+ }
+
+
+ /// <summary>
+ /// 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 <paramref name="requestType"/> parameter. Alternatively, <see cref="ControlIn(byte,byte,int,int,byte[],int)"/> and
+ /// <see cref="ControlOut(byte,byte,int,int,byte[],int)"/> 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.
+ /// </summary>
+ /// <param name="requestType">The setup packet request type.</param>
+ /// <param name="request">The setup packet device request.</param>
+ /// <param name="value">The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF).</param>
+ /// <param name="index">The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF).</param>
+ /// <param name="buffer">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.</param>
+ /// <param name="length">Length of the data to transfer. Must be equal to or less than the length of <paramref name="buffer"/>.
+ /// The setup packet's length member will be set to this length.</param>
+ 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);
+ }
+ }
+
+ /// <summary>
+ /// 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 <paramref name="requestType"/> parameter. Alternatively, <see cref="BeginControlIn(byte,byte,int,int,byte[],int,AsyncCallback,object)"/> and
+ /// <see cref="BeginControlIn(byte,byte,int,int,byte[],int,AsyncCallback,object)"/> 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. </summary>
+ /// <param name="requestType">The setup packet request type.</param>
+ /// <param name="request">The setup packet device request.</param>
+ /// <param name="value">The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF).</param>
+ /// <param name="index">The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF).</param>
+ /// <param name="buffer">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.</param>
+ /// <param name="length">Length of the data to transfer. Must be equal to or less than the length of <paramref name="buffer"/>. The setup packet's length member will be set to this length.</param>
+ /// <param name="userCallback">An optional asynchronous callback, to be called when the control transfer is complete. Can be null if no callback is required.</param>
+ /// <param name="stateObject">A user-provided object that distinguishes this particular asynchronous operation. Can be null if not required.</param>
+ /// <returns>An <see cref="IAsyncResult"/> object repesenting the asynchronous control transfer, which could still be pending.</returns>
+ /// <remarks>This method always completes immediately even if the operation is still pending. The <see cref="IAsyncResult"/> object returned represents the operation
+ /// and must be passed to <see cref="EndControlTransfer"/> to retrieve the result of the operation. For every call to this method a matching call to
+ /// <see cref="EndControlTransfer"/> must be made. When <paramref name="userCallback"/> specifies a callback function, this function will be called when the operation is completed. The optional
+ /// <paramref name="stateObject"/> parameter can be used to pass user-defined information to this callback or the <see cref="IAsyncResult"/>. The <see cref="IAsyncResult"/>
+ /// also provides an event handle (<see cref="IAsyncResult.AsyncWaitHandle" />) that will be triggered when the operation is complete as well.
+ /// </remarks>
+ 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;
+ }
+
+ /// <summary>
+ /// 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 <paramref name="requestType"/> parameter. Alternatively, <see cref="BeginControlIn(byte,byte,int,int,byte[],int,AsyncCallback,object)"/> and
+ /// <see cref="BeginControlIn(byte,byte,int,int,byte[],int,AsyncCallback,object)"/> 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. </summary>
+ /// <param name="requestType">The setup packet request type.</param>
+ /// <param name="request">The setup packet device request.</param>
+ /// <param name="value">The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF).</param>
+ /// <param name="index">The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF).</param>
+ /// <param name="buffer">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. </param>
+ /// <param name="userCallback">An optional asynchronous callback, to be called when the control transfer is complete. Can be null if no callback is required.</param>
+ /// <param name="stateObject">A user-provided object that distinguishes this particular asynchronous operation. Can be null if not required.</param>
+ /// <returns>An <see cref="IAsyncResult"/> object repesenting the asynchronous control transfer, which could still be pending.</returns>
+ /// <remarks>This method always completes immediately even if the operation is still pending. The <see cref="IAsyncResult"/> object returned represents the operation
+ /// and must be passed to <see cref="EndControlTransfer"/> to retrieve the result of the operation. For every call to this method a matching call to
+ /// <see cref="EndControlTransfer"/> must be made. When <paramref name="userCallback"/> specifies a callback function, this function will be called when the operation is completed. The optional
+ /// <paramref name="stateObject"/> parameter can be used to pass user-defined information to this callback or the <see cref="IAsyncResult"/>. The <see cref="IAsyncResult"/>
+ /// also provides an event handle (<see cref="IAsyncResult.AsyncWaitHandle" />) that will be triggered when the operation is complete as well.
+ /// </remarks>
+ 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);
+ }
+
+
+ /// <summary>
+ /// Waits for a pending asynchronous control transfer to complete.
+ /// </summary>
+ /// <param name="asyncResult">The <see cref="IAsyncResult"/> object representing the asynchonous operation,
+ /// as returned by one of the ControlIn, ControlOut or ControlTransfer methods.</param>
+ /// <returns>The number of bytes transfered during the operation.</returns>
+ /// <remarks>Every asynchronous control transfer must have a matching call to <see cref="EndControlTransfer"/> 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.</remarks>
+ 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();
+ }
+
+ }
+
+
+ /// <summary>
+ /// 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 <paramref name="requestType"/> parameter). Alternatively, <see cref="ControlIn(byte,byte,int,int,byte[])"/> and
+ /// <see cref="ControlOut(byte,byte,int,int,byte[])"/> 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.
+ /// </summary>
+ /// <param name="requestType">The setup packet request type.</param>
+ /// <param name="request">The setup packet device request.</param>
+ /// <param name="value">The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF).</param>
+ /// <param name="index">The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF).</param>
+ /// <param name="buffer">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.</param>
+ public void ControlTransfer(byte requestType, byte request, int value, int index, byte[] buffer)
+ {
+ ControlTransfer(requestType, request, value, index, buffer, buffer.Length);
+ }
+
+ /// <summary>
+ /// 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 <paramref name="requestType"/> parameter). Alternatively, <see cref="ControlIn(byte,byte,int,int)"/> and
+ /// <see cref="ControlOut(byte,byte,int,int)"/> 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.
+ /// </summary>
+ /// <param name="requestType">The setup packet request type.</param>
+ /// <param name="request">The setup packet device request.</param>
+ /// <param name="value">The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF).</param>
+ /// <param name="index">The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF).</param>
+ 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.");
+ }
+
+ /// <summary>
+ /// Initiates a control transfer over the default control endpoint. The request should have an IN direction (specified by the highest bit
+ /// of the <paramref name="requestType"/> parameter). A buffer to receive the data is automatically created by this method.
+ /// </summary>
+ /// <param name="requestType">The setup packet request type. The request type must specify the IN direction (highest bit set).</param>
+ /// <param name="request">The setup packet device request.</param>
+ /// <param name="value">The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF).</param>
+ /// <param name="index">The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF).</param>
+ /// <param name="length">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.</param>
+ /// <returns>A buffer containing the data transfered.</returns>
+ 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;
+ }
+
+
+ /// <summary>
+ /// Initiates a control transfer over the default control endpoint. The request should have an IN direction (specified by the highest bit
+ /// of the <paramref name="requestType"/> parameter).
+ /// </summary>
+ /// <param name="requestType">The setup packet request type. The request type must specify the IN direction (highest bit set).</param>
+ /// <param name="request">The setup packet device request.</param>
+ /// <param name="value">The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF).</param>
+ /// <param name="index">The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF).</param>
+ /// <param name="buffer">The buffer that will receive the data transfered.</param>
+ /// <param name="length">Length of the data to transfer. The length member of the setup packet will be set to this length. The buffer specified
+ /// by the <paramref name="buffer"/> parameter should have at least this length.</param>
+ public void ControlIn(byte requestType, byte request, int value, int index, byte[] buffer, int length)
+ {
+ CheckIn(requestType);
+ ControlTransfer(requestType, request, value, index, buffer, length);
+ }
+
+ /// <summary>
+ /// Initiates a control transfer over the default control endpoint. The request should have an IN direction (specified by the highest bit
+ /// of the <paramref name="requestType"/> parameter). The length of buffer given by the <paramref name="buffer"/> parameter will dictate
+ /// the number of bytes that are transfered and the value of the setup packet's length member.
+ /// </summary>
+ /// <param name="requestType">The setup packet request type. The request type must specify the IN direction (highest bit set).</param>
+ /// <param name="request">The setup packet device request.</param>
+ /// <param name="value">The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF).</param>
+ /// <param name="index">The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF).</param>
+ /// <param name="buffer">The buffer that will receive the data transfered. The length of this buffer will be the number of bytes transfered.</param>
+ public void ControlIn(byte requestType, byte request, int value, int index, byte[] buffer)
+ {
+ CheckIn(requestType);
+ ControlTransfer(requestType, request, value, index, buffer);
+ }
+
+ /// <summary>
+ /// 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 <paramref name="requestType"/> parameter). The setup packets' length member will be set to zero.
+ /// </summary>
+ /// <param name="requestType">The setup packet request type. The request type must specify the IN direction (highest bit set).</param>
+ /// <param name="request">The setup packet device request.</param>
+ /// <param name="value">The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF).</param>
+ /// <param name="index">The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF).</param>
+ 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]);
+ }
+
+ /// <summary>
+ /// Initiates a control transfer over the default control endpoint. The request should have an OUT direction (specified by the highest bit
+ /// of the <paramref name="requestType"/> parameter).
+ /// </summary>
+ /// <param name="requestType">The setup packet request type. The request type must specify the OUT direction (highest bit cleared).</param>
+ /// <param name="request">The setup packet device request.</param>
+ /// <param name="value">The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF).</param>
+ /// <param name="index">The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF).</param>
+ /// <param name="buffer">A buffer containing the data to transfer in the data stage.</param>
+ /// <param name="length">Length of the data to transfer. Only the first <paramref name="length"/> bytes of <paramref name="buffer"/> will be transfered.
+ /// The setup packet's length parameter is set to this length.</param>
+ public void ControlOut(byte requestType, byte request, int value, int index, byte[] buffer, int length)
+ {
+ CheckOut(requestType);
+ ControlTransfer(requestType, request, value, index, buffer, length);
+ }
+
+ /// <summary>
+ /// Initiates a control transfer over the default control endpoint. The request should have an OUT direction (specified by the highest bit
+ /// of the <paramref name="requestType"/> parameter).
+ /// </summary>
+ /// <param name="requestType">The setup packet request type. The request type must specify the OUT direction (highest bit cleared).</param>
+ /// <param name="request">The setup packet device request.</param>
+ /// <param name="value">The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF).</param>
+ /// <param name="index">The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF).</param>
+ /// <param name="buffer">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.</param>
+ public void ControlOut(byte requestType, byte request, int value, int index, byte[] buffer)
+ {
+ CheckOut(requestType);
+ ControlTransfer(requestType, request, value, index, buffer);
+ }
+
+ /// <summary>
+ /// 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 <paramref name="requestType"/> parameter. The setup packets' length member will be set to zero.
+ /// </summary>
+ /// <param name="requestType">The setup packet request type. The request type must specify the OUT direction (highest bit cleared).</param>
+ /// <param name="request">The setup packet device request.</param>
+ /// <param name="value">The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF).</param>
+ /// <param name="index">The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF).</param>
+ 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]);
+ }
+
+
+
+ /// <summary>
+ /// 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 <paramref name="requestType"/> parameter. Alternatively, <see cref="BeginControlIn(byte,byte,int,int,byte[],int,AsyncCallback,object)"/> and
+ /// <see cref="BeginControlIn(byte,byte,int,int,byte[],int,AsyncCallback,object)"/> 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. </summary>
+ /// <param name="requestType">The setup packet request type.</param>
+ /// <param name="request">The setup packet device request.</param>
+ /// <param name="value">The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF).</param>
+ /// <param name="index">The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF).</param>
+ /// <param name="userCallback">An optional asynchronous callback, to be called when the control transfer is complete. Can be null if no callback is required.</param>
+ /// <param name="stateObject">A user-provided object that distinguishes this particular asynchronous operation. Can be null if not required.</param>
+ /// <returns>An <see cref="IAsyncResult"/> object repesenting the asynchronous control transfer, which could still be pending.</returns>
+ /// <remarks>This method always completes immediately even if the operation is still pending. The <see cref="IAsyncResult"/> object returned represents the operation
+ /// and must be passed to <see cref="EndControlTransfer"/> to retrieve the result of the operation. For every call to this method a matching call to
+ /// <see cref="EndControlTransfer"/> must be made. When <paramref name="userCallback"/> specifies a callback function, this function will be called when the operation is completed. The optional
+ /// <paramref name="stateObject"/> parameter can be used to pass user-defined information to this callback or the <see cref="IAsyncResult"/>. The <see cref="IAsyncResult"/>
+ /// also provides an event handle (<see cref="IAsyncResult.AsyncWaitHandle" />) that will be triggered when the operation is complete as well.
+ /// </remarks>
+ 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);
+ }
+
+
+
+ /// <summary>
+ /// Initiates an asynchronous control transfer over the default control endpoint. The request should have an IN direction (specified by the highest bit
+ /// of the <paramref name="requestType"/> parameter).</summary>
+ /// <param name="requestType">The setup packet request type. The request type must specify the IN direction (highest bit set).</param>
+ /// <param name="request">The setup packet device request.</param>
+ /// <param name="value">The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF).</param>
+ /// <param name="index">The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF).</param>
+ /// <param name="buffer">The buffer that will receive the data transfered.</param>
+ /// <param name="length">Length of the data to transfer. Must be equal to or less than the length of <paramref name="buffer"/>. The setup packet's length member will be set to this length.</param>
+ /// <param name="userCallback">An optional asynchronous callback, to be called when the control transfer is complete. Can be null if no callback is required.</param>
+ /// <param name="stateObject">A user-provided object that distinguishes this particular asynchronous operation. Can be null if not required.</param>
+ /// <returns>An <see cref="IAsyncResult"/> object repesenting the asynchronous control transfer, which could still be pending.</returns>
+ /// <remarks>This method always completes immediately even if the operation is still pending. The <see cref="IAsyncResult"/> object returned represents the operation
+ /// and must be passed to <see cref="EndControlTransfer"/> to retrieve the result of the operation. For every call to this method a matching call to
+ /// <see cref="EndControlTransfer"/> must be made. When <paramref name="userCallback"/> specifies a callback function, this function will be called when the operation is completed. The optional
+ /// <paramref name="stateObject"/> parameter can be used to pass user-defined information to this callback or the <see cref="IAsyncResult"/>. The <see cref="IAsyncResult"/>
+ /// also provides an event handle (<see cref="IAsyncResult.AsyncWaitHandle" />) that will be triggered when the operation is complete as well.
+ /// </remarks>
+ 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);
+ }
+
+ /// <summary>
+ /// Initiates an asynchronous control transfer over the default control endpoint. The request should have an IN direction (specified by the highest bit
+ /// of the <paramref name="requestType"/> parameter).</summary>
+ /// <param name="requestType">The setup packet request type. The request type must specify the IN direction (highest bit set).</param>
+ /// <param name="request">The setup packet device request.</param>
+ /// <param name="value">The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF).</param>
+ /// <param name="index">The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF).</param>
+ /// <param name="buffer">The buffer that will receive the data transfered. The setup packet's length member will be set to the length of this buffer.</param>
+ /// <param name="userCallback">An optional asynchronous callback, to be called when the control transfer is complete. Can be null if no callback is required.</param>
+ /// <param name="stateObject">A user-provided object that distinguishes this particular asynchronous operation. Can be null if not required.</param>
+ /// <returns>An <see cref="IAsyncResult"/> object repesenting the asynchronous control transfer, which could still be pending.</returns>
+ /// <remarks>This method always completes immediately even if the operation is still pending. The <see cref="IAsyncResult"/> object returned represents the operation
+ /// and must be passed to <see cref="EndControlTransfer"/> to retrieve the result of the operation. For every call to this method a matching call to
+ /// <see cref="EndControlTransfer"/> must be made. When <paramref name="userCallback"/> specifies a callback function, this function will be called when the operation is completed. The optional
+ /// <paramref name="stateObject"/> parameter can be used to pass user-defined information to this callback or the <see cref="IAsyncResult"/>. The <see cref="IAsyncResult"/>
+ /// also provides an event handle (<see cref="IAsyncResult.AsyncWaitHandle" />) that will be triggered when the operation is complete as well.
+ /// </remarks>
+ 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);
+ }
+
+ /// <summary>
+ /// 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 <paramref name="requestType"/> parameter).
+ /// The setup packets' length member will be set to zero.</summary>
+ /// <param name="requestType">The setup packet request type. The request type must specify the IN direction (highest bit set).</param>
+ /// <param name="request">The setup packet device request.</param>
+ /// <param name="value">The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF).</param>
+ /// <param name="index">The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF).</param>
+ /// <param name="userCallback">An optional asynchronous callback, to be called when the control transfer is complete. Can be null if no callback is required.</param>
+ /// <param name="stateObject">A user-provided object that distinguishes this particular asynchronous operation. Can be null if not required.</param>
+ /// <returns>An <see cref="IAsyncResult"/> object repesenting the asynchronous control transfer, which could still be pending.</returns>
+ /// <remarks>This method always completes immediately even if the operation is still pending. The <see cref="IAsyncResult"/> object returned represents the operation
+ /// and must be passed to <see cref="EndControlTransfer"/> to retrieve the result of the operation. For every call to this method a matching call to
+ /// <see cref="EndControlTransfer"/> must be made. When <paramref name="userCallback"/> specifies a callback function, this function will be called when the operation is completed. The optional
+ /// <paramref name="stateObject"/> parameter can be used to pass user-defined information to this callback or the <see cref="IAsyncResult"/>. The <see cref="IAsyncResult"/>
+ /// also provides an event handle (<see cref="IAsyncResult.AsyncWaitHandle" />) that will be triggered when the operation is complete as well.
+ /// </remarks>
+ 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);
+ }
+
+ /// <summary>
+ /// Initiates an asynchronous control transfer over the default control endpoint. The request should have an OUT direction (specified by the highest bit
+ /// of the <paramref name="requestType"/> parameter).</summary>
+ /// <param name="requestType">The setup packet request type. The request type must specify the OUT direction (highest bit cleared).</param>
+ /// <param name="request">The setup packet device request.</param>
+ /// <param name="value">The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF).</param>
+ /// <param name="index">The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF).</param>
+ /// <param name="buffer">The buffer that contains the data to be transfered.</param>
+ /// <param name="length">Length of the data to transfer. Must be equal to or less than the length of <paramref name="buffer"/>. The setup packet's length member will be set to this length.</param>
+ /// <param name="userCallback">An optional asynchronous callback, to be called when the control transfer is complete. Can be null if no callback is required.</param>
+ /// <param name="stateObject">A user-provided object that distinguishes this particular asynchronous operation. Can be null if not required.</param>
+ /// <returns>An <see cref="IAsyncResult"/> object repesenting the asynchronous control transfer, which could still be pending.</returns>
+ /// <remarks>This method always completes immediately even if the operation is still pending. The <see cref="IAsyncResult"/> object returned represents the operation
+ /// and must be passed to <see cref="EndControlTransfer"/> to retrieve the result of the operation. For every call to this method a matching call to
+ /// <see cref="EndControlTransfer"/> must be made. When <paramref name="userCallback"/> specifies a callback function, this function will be called when the operation is completed. The optional
+ /// <paramref name="stateObject"/> parameter can be used to pass user-defined information to this callback or the <see cref="IAsyncResult"/>. The <see cref="IAsyncResult"/>
+ /// also provides an event handle (<see cref="IAsyncResult.AsyncWaitHandle" />) that will be triggered when the operation is complete as well.
+ /// </remarks>
+ 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);
+ }
+
+ /// <summary>
+ /// Initiates an asynchronous control transfer over the default control endpoint. The request should have an OUT direction (specified by the highest bit
+ /// of the <paramref name="requestType"/> parameter).</summary>
+ /// <param name="requestType">The setup packet request type. The request type must specify the OUT direction (highest bit cleared).</param>
+ /// <param name="request">The setup packet device request.</param>
+ /// <param name="value">The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF).</param>
+ /// <param name="index">The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF).</param>
+ /// <param name="buffer">The buffer that contains the data to be transfered. The setup packet's length member will be set to the length of this buffer.</param>
+ /// <param name="userCallback">An optional asynchronous callback, to be called when the control transfer is complete. Can be null if no callback is required.</param>
+ /// <param name="stateObject">A user-provided object that distinguishes this particular asynchronous operation. Can be null if not required.</param>
+ /// <returns>An <see cref="IAsyncResult"/> object repesenting the asynchronous control transfer, which could still be pending.</returns>
+ /// <remarks>This method always completes immediately even if the operation is still pending. The <see cref="IAsyncResult"/> object returned represents the operation
+ /// and must be passed to <see cref="EndControlTransfer"/> to retrieve the result of the operation. For every call to this method a matching call to
+ /// <see cref="EndControlTransfer"/> must be made. When <paramref name="userCallback"/> specifies a callback function, this function will be called when the operation is completed. The optional
+ /// <paramref name="stateObject"/> parameter can be used to pass user-defined information to this callback or the <see cref="IAsyncResult"/>. The <see cref="IAsyncResult"/>
+ /// also provides an event handle (<see cref="IAsyncResult.AsyncWaitHandle" />) that will be triggered when the operation is complete as well.
+ /// </remarks>
+ 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);
+ }
+
+ /// <summary>
+ /// 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 <paramref name="requestType"/> parameter).
+ /// The setup packets' length member will be set to zero.</summary>
+ /// <param name="requestType">The setup packet request type. The request type must specify the OUT direction (highest bit cleared).</param>
+ /// <param name="request">The setup packet device request.</param>
+ /// <param name="value">The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF).</param>
+ /// <param name="index">The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF).</param>
+ /// <param name="userCallback">An optional asynchronous callback, to be called when the control transfer is complete. Can be null if no callback is required.</param>
+ /// <param name="stateObject">A user-provided object that distinguishes this particular asynchronous operation. Can be null if not required.</param>
+ /// <returns>An <see cref="IAsyncResult"/> object repesenting the asynchronous control transfer, which could still be pending.</returns>
+ /// <remarks>This method always completes immediately even if the operation is still pending. The <see cref="IAsyncResult"/> object returned represents the operation
+ /// and must be passed to <see cref="EndControlTransfer"/> to retrieve the result of the operation. For every call to this method a matching call to
+ /// <see cref="EndControlTransfer"/> must be made. When <paramref name="userCallback"/> specifies a callback function, this function will be called when the operation is completed. The optional
+ /// <paramref name="stateObject"/> parameter can be used to pass user-defined information to this callback or the <see cref="IAsyncResult"/>. The <see cref="IAsyncResult"/>
+ /// also provides an event handle (<see cref="IAsyncResult.AsyncWaitHandle" />) that will be triggered when the operation is complete as well.
+ /// </remarks>
+ 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.");
+ }
+
+ /// <summary>
+ /// Finds USB devices
+ /// </summary>
+ 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;
+ }
+
+ /// <summary>
+ /// Finds the first WinUSB device with a VIA and PID
+ /// </summary>
+ 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
+{
+ /// <summary>
+ /// USB device details
+ /// </summary>
+ public class USBDeviceDescriptor
+ {
+ /// <summary>
+ /// Windows path name for the USB device
+ /// </summary>
+ public string PathName { get; private set; }
+
+ /// <summary>
+ /// USB vendor ID (VID) of the device
+ /// </summary>
+ public int VID { get; private set; }
+
+ /// <summary>
+ /// USB product ID (PID) of the device
+ /// </summary>
+ public int PID { get; private set; }
+
+ /// <summary>
+ /// Manufacturer name, or null if not available
+ /// </summary>
+ public string Manufacturer { get; private set; }
+
+ /// <summary>
+ /// Product name, or null if not available
+ /// </summary>
+ public string Product { get; private set; }
+
+ /// <summary>
+ /// Device serial number, or null if not available
+ /// </summary>
+ public string SerialNumber { get; private set; }
+
+
+ /// <summary>
+ /// Friendly device name, or path name when no
+ /// further device information is available
+ /// </summary>
+ 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;
+ }
+ }
+
+ /// <summary>
+ /// 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
+ /// </summary>
+ public byte ClassValue
+ {
+ get;
+ private set;
+ }
+
+ /// <summary>
+ /// Device subclass code
+ /// </summary>
+ public byte SubClass
+ {
+ get;
+ private set;
+ }
+
+ /// <summary>
+ /// Device protocol code
+ /// </summary>
+ public byte Protocol
+ {
+ get;
+ private set;
+ }
+
+ /// <summary>
+ /// Device class code. If the device class does
+ /// not match any of the USBBaseClass enumeration values
+ /// the value will be USBBaseClass.Unknown
+ /// </summary>
+ 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
+{
+ /// <summary>
+ /// 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.
+ /// </summary>
+ public class USBDeviceInfo
+ {
+ private API.DeviceDetails _details;
+
+ /// <summary>
+ /// Vendor ID (VID) of the USB device
+ /// </summary>
+ public int VID
+ {
+ get
+ {
+ return _details.VID;
+ }
+ }
+
+ /// <summary>
+ /// Product ID (VID) of the USB device
+ /// </summary>
+ public int PID
+ {
+ get
+ {
+ return _details.PID;
+ }
+ }
+
+ /// <summary>
+ /// Manufacturer of the device, as specified in the INF file (not the device descriptor)
+ /// </summary>
+ public string Manufacturer
+ {
+ get
+ {
+ return _details.Manufacturer;
+ }
+ }
+
+ /// <summary>
+ /// Description of the device, as specified in the INF file (not the device descriptor)
+ /// </summary>
+ public string DeviceDescription
+ {
+ get
+ {
+ return _details.DeviceDescription;
+ }
+ }
+
+ /// <summary>
+ /// Device pathname
+ /// </summary>
+ 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
+{
+ /// <summary>
+ /// Exception used by WinUSBNet to indicate errors. This is the
+ /// main exception to catch when using the library.
+ /// </summary>
+ public class USBException : Exception
+ {
+ /// <summary>
+ /// Constructs a new USBException with the given message
+ /// </summary>
+ /// <param name="message">The message describing the exception</param>
+ public USBException(string message)
+ : base(message)
+ {
+ }
+
+ /// <summary>
+ /// Constructs a new USBException with the given message and underlying exception
+ /// that caused the USBException.
+ /// </summary>
+ /// <param name="message">The message describing the exception</param>
+ /// <param name="innerException">The underlying exception causing the USBException</param>
+ 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
+{
+ /// <summary>
+ /// Represents a single USB interface from a USB device
+ /// </summary>
+ public class USBInterface
+ {
+ /// <summary>
+ /// Collection of pipes associated with this interface
+ /// </summary>
+ public USBPipeCollection Pipes
+ {
+ get;
+ private set;
+ }
+
+ /// <summary>
+ /// Interface number from the interface descriptor
+ /// </summary>
+ public int Number
+ {
+ get;
+ private set;
+ }
+
+ /// <summary>
+ /// USB device associated with this interface
+ /// </summary>
+ public USBDevice Device
+ {
+ get;
+ private set;
+ }
+
+ /// <summary>
+ /// First IN direction pipe on this interface
+ /// </summary>
+ public USBPipe InPipe
+ {
+ get;
+ private set;
+ }
+
+ /// <summary>
+ /// First OUT direction pipe on this interface
+ /// </summary>
+ public USBPipe OutPipe
+ {
+ get;
+ private set;
+ }
+
+ /// <summary>
+ /// Interface class code. If the interface class does
+ /// not match any of the USBBaseClass enumeration values
+ /// the value will be USBBaseClass.Unknown
+ /// </summary>
+ public USBBaseClass BaseClass
+ {
+ get;
+ private set;
+ }
+
+ /// <summary>
+ /// 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
+ /// </summary>
+ public byte ClassValue
+ {
+ get;
+ private set;
+ }
+
+ /// <summary>
+ /// Interface subclass code
+ /// </summary>
+ public byte SubClass
+ {
+ get;
+ private set;
+ }
+
+ /// <summary>
+ /// Interface protocol code
+ /// </summary>
+ 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
+{
+ /// <summary>
+ /// Collection of UsbInterface objects
+ /// </summary>
+ public class USBInterfaceCollection : IEnumerable<USBInterface>
+ {
+ private USBInterface[] _interfaces;
+
+ internal USBInterfaceCollection(USBInterface[] interfaces)
+ {
+ _interfaces = interfaces;
+ }
+
+ private class USBInterfaceEnumerator : IEnumerator<USBInterface>
+ {
+ 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;
+ }
+ }
+
+ /// <summary>
+ /// Finds the first interface with that matches the device class
+ /// given by the <paramref name="interfaceClass"/> parameter.
+ /// </summary>
+ /// <param name="interfaceClass">The device class the interface should match</param>
+ /// <returns>The first interface with the given interface class, or null
+ /// if no such interface exists.</returns>
+ 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;
+ }
+
+ /// <summary>
+ /// Finds all interfaces matching the device class given by the
+ /// <paramref name="interfaceClass"/> parameter.
+ /// </summary>
+ /// <param name="interfaceClass">The device class the interface should match</param>
+ /// <returns>An array of USBInterface objects matching the device class, or an empty
+ /// array if no interface matches.</returns>
+ public USBInterface[] FindAll(USBBaseClass interfaceClass)
+ {
+ List<USBInterface> matchingInterfaces = new List<USBInterface>();
+ for (int i = 0; i < _interfaces.Length; i++)
+ {
+ USBInterface iface = _interfaces[i];
+ if (iface.BaseClass == interfaceClass)
+ matchingInterfaces.Add(iface);
+ }
+ return matchingInterfaces.ToArray();
+ }
+
+ /// <summary>
+ /// Returns a typed enumerator that iterates through a collection.
+ /// </summary>
+ /// <returns>The enumerator object that can be used to iterate through the collection.</returns>
+ public IEnumerator<USBInterface> GetEnumerator()
+ {
+ return new USBInterfaceEnumerator(_interfaces);
+ }
+
+ /// <summary>
+ /// Get interface by interface number
+ /// </summary>
+ /// <param name="interfaceNumber">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.</param>
+ /// <exception cref="IndexOutOfRangeException">Thrown when the given interface number does not exist in the collection.</exception>
+ /// <returns></returns>
+ 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));
+ }
+ }
+
+ /// <summary>
+ /// Returns an enumerator that iterates through a collection.
+ /// </summary>
+ /// <returns>An IEnumerator object that can be used to iterate through the collection.</returns>
+ 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
+{
+ /// <summary>
+ /// Delegate for event handler methods handing USB events
+ /// </summary>
+ /// <param name="sender">The source of the event</param>
+ /// <param name="e">Details of the event</param>
+ public delegate void USBEventHandler(object sender, USBEvent e);
+
+ /// <summary>
+ /// Event type enumeration for WinUSB events
+ /// </summary>
+ public enum USBEventType
+ {
+ /// <summary>
+ /// A device has been connected to the system
+ /// </summary>
+ DeviceArrival,
+
+ /// <summary>
+ /// A device has been disconnected from the system
+ /// </summary>
+ DeviceRemoval,
+ }
+
+ /// <summary>
+ /// Contains the details of a USB event
+ /// </summary>
+ public class USBEvent : EventArgs
+ {
+ /// <summary>
+ /// WinUSB interface GUID of the device as specified in the WinUSBNotifier
+ /// </summary>
+ public Guid Guid;
+
+ /// <summary>
+ /// Device pathname that identifies the device
+ /// </summary>
+ public string DevicePath;
+
+ /// <summary>
+ /// Type of event that occurred
+ /// </summary>
+ public USBEventType Type;
+
+ internal USBEvent(USBEventType type, Guid guid, string devicePath)
+ {
+ this.Guid = guid;
+ this.DevicePath = devicePath;
+ this.Type= type;
+ }
+ }
+
+ /// <summary>
+ /// Helper class to receive notifications on USB device changes such as
+ /// connecting or removing a device.
+ /// </summary>
+ public class USBNotifier : IDisposable
+ {
+ private DeviceNotifyHook _hook;
+ private Guid _guid;
+
+ /// <summary>
+ /// Event triggered when a new USB device that matches the USBNotifier's GUID is connected
+ /// </summary>
+ public event USBEventHandler Arrival;
+
+ /// <summary>
+ /// Event triggered when a new USB device that matches the USBNotifier's GUID is disconnected
+ /// </summary>
+ public event USBEventHandler Removal;
+
+ /// <summary>
+ /// The interface GUID of devices this USBNotifier will watch
+ /// </summary>
+ public Guid Guid
+ {
+ get
+ {
+ return _guid;
+ }
+ }
+
+ /// <summary>
+ /// 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.
+ /// </summary>
+ /// <param name="control">A control that will be used internally for device notification messages.
+ /// You can use a Form object for example.</param>
+ /// <param name="guidString">The interface GUID string of the devices to watch.</param>
+ public USBNotifier(Control control, string guidString) :
+ this(control, new Guid(guidString))
+ {
+ // Handled in other constructor
+ }
+
+
+ /// <summary>
+ /// 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.
+ /// </summary>
+ /// <param name="control">A control that will be used internally for device notification messages.
+ /// You can use a Form object for example.</param>
+ /// <param name="guid">The interface GUID of the devices to watch.</param>
+ public USBNotifier(Control control, Guid guid)
+ {
+ _guid = guid;
+ _hook = new DeviceNotifyHook(this, control, _guid);
+ }
+
+ /// <summary>
+ /// Triggers the arrival event
+ /// </summary>
+ /// <param name="devicePath">Device pathname of the device that has been connected</param>
+ protected void OnArrival(string devicePath)
+ {
+ if (Arrival != null)
+ Arrival(this, new USBEvent(USBEventType.DeviceArrival, _guid, devicePath));
+ }
+ /// <summary>
+ /// Trigggers the removal event
+ /// </summary>
+ /// <param name="devicePath">Device pathname of the device that has been connected</param>
+ 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);
+ }
+
+ }
+
+ /// <summary>
+ /// Disposes the USBNotifier object and frees all resources.
+ /// Call this method when the object is no longer needed.
+ /// </summary>
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ /// <summary>
+ /// Disposes the object's resources.
+ /// </summary>
+ /// <param name="disposing">True when dispose is called manually, false when called by the finalizer.</param>
+ 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
+{
+ /// <summary>
+ /// 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)
+ /// </summary>
+ public class USBPipe
+ {
+ private API.WINUSB_PIPE_INFORMATION _pipeInfo;
+ private USBInterface _interface = null;
+ private USBDevice _device;
+ private USBPipePolicy _policy;
+
+ /// <summary>
+ /// Endpoint address including the direction in the most significant bit
+ /// </summary>
+ public byte Address
+ {
+ get
+ {
+ return _pipeInfo.PipeId;
+ }
+ }
+
+ /// <summary>
+ /// The USBDevice this pipe is associated with
+ /// </summary>
+ public USBDevice Device
+ {
+ get
+ {
+ return _device;
+ }
+ }
+
+ /// <summary>
+ /// Maximum packet size for transfers on this endpoint
+ /// </summary>
+ public int MaximumPacketSize
+ {
+ get
+ {
+ return _pipeInfo.MaximumPacketSize;
+ }
+ }
+
+ /// <summary>
+ /// The interface associated with this pipe
+ /// </summary>
+ public USBInterface Interface
+ {
+ get
+ {
+ return _interface;
+ }
+ }
+
+ /// <summary>
+ /// The pipe policy settings for this pipe
+ /// </summary>
+ public USBPipePolicy Policy
+ {
+ get
+ {
+ return _policy;
+ }
+ }
+
+ /// <summary>
+ /// True if the pipe has direction OUT (host to device), false otherwise.
+ /// </summary>
+ public bool IsOut
+ {
+ get
+ {
+ return (_pipeInfo.PipeId & 0x80) == 0;
+ }
+ }
+
+ /// <summary>
+ /// True if the pipe has direction IN (device to host), false otherwise.
+ /// </summary>
+ public bool IsIn
+ {
+ get
+ {
+ return (_pipeInfo.PipeId & 0x80) != 0;
+ }
+ }
+
+ /// <summary>
+ /// Reads data from the pipe into a buffer.
+ /// </summary>
+ /// <param name="buffer">The buffer to read data into. The maximum number of bytes that will be read is specified by the length of the buffer.</param>
+ /// <returns>The number of bytes read from the pipe.</returns>
+ public int Read(byte[] buffer)
+ {
+ return Read(buffer, 0, buffer.Length);
+ }
+
+ /// <summary>
+ /// Reads data from the pipe into a buffer.
+ /// </summary>
+ /// <param name="buffer">The buffer to read data into.</param>
+ /// <param name="offset">The byte offset in <paramref name="buffer"/> from which to begin writing data read from the pipe.</param>
+ /// <param name="length">The maximum number of bytes to read, starting at offset</param>
+ /// <returns>The number of bytes read from the pipe.</returns>
+ 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.");
+
+ }
+
+ /// <summary>Initiates an asynchronous read operation on the pipe. </summary>
+ /// <param name="buffer">Buffer that will receive the data read from the pipe.</param>
+ /// <param name="offset">Byte offset within the buffer at which to begin writing the data received.</param>
+ /// <param name="length">Length of the data to transfer.</param>
+ /// <param name="userCallback">An optional asynchronous callback, to be called when the operation is complete. Can be null if no callback is required.</param>
+ /// <param name="stateObject">A user-provided object that distinguishes this particular asynchronous operation. Can be null if not required.</param>
+ /// <returns>An <see cref="IAsyncResult"/> object repesenting the asynchronous operation, which could still be pending.</returns>
+ /// <remarks>This method always completes immediately even if the operation is still pending. The <see cref="IAsyncResult"/> object returned represents the operation
+ /// and must be passed to <see cref="EndRead"/> to retrieve the result of the operation. For every call to this method a matching call to
+ /// <see cref="EndRead"/> must be made. When <paramref name="userCallback"/> specifies a callback function, this function will be called when the operation is completed. The optional
+ /// <paramref name="stateObject"/> parameter can be used to pass user-defined information to this callback or the <see cref="IAsyncResult"/>. The <see cref="IAsyncResult"/>
+ /// also provides an event handle (<see cref="IAsyncResult.AsyncWaitHandle" />) that will be triggered when the operation is complete as well.
+ /// </remarks>
+ 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;
+ }
+
+ /// <summary>
+ /// Waits for a pending asynchronous read operation to complete.
+ /// </summary>
+ /// <param name="asyncResult">The <see cref="IAsyncResult"/> object representing the asynchonous operation,
+ /// as returned by <see cref="BeginRead"/>.</param>
+ /// <returns>The number of bytes transfered during the operation.</returns>
+ /// <remarks>Every call to <see cref="BeginRead"/> must have a matching call to <see cref="EndRead"/> 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.</remarks>
+ 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();
+ }
+
+ }
+
+ /// <summary>
+ /// Writes data from a buffer to the pipe.
+ /// </summary>
+ /// <param name="buffer">The buffer to write data from. The complete buffer will be written to the device.</param>
+ public void Write(byte[] buffer)
+ {
+ Write(buffer, 0, buffer.Length);
+ }
+
+ /// <summary>
+ /// Writes data from a buffer to the pipe.
+ /// </summary>
+ /// <param name="buffer">The buffer to write data from.</param>
+ /// <param name="offset">The byte offset in <paramref name="buffer"/> from which to begin writing.</param>
+ /// <param name="length">The number of bytes to write, starting at offset</param>
+ 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);
+ }
+ }
+
+ /// <summary>Initiates an asynchronous write operation on the pipe. </summary>
+ /// <param name="buffer">Buffer that contains the data to write to the pipe.</param>
+ /// <param name="offset">Byte offset within the buffer from which to begin writing.</param>
+ /// <param name="length">Length of the data to transfer.</param>
+ /// <param name="userCallback">An optional asynchronous callback, to be called when the operation is complete. Can be null if no callback is required.</param>
+ /// <param name="stateObject">A user-provided object that distinguishes this particular asynchronous operation. Can be null if not required.</param>
+ /// <returns>An <see cref="IAsyncResult"/> object repesenting the asynchronous operation, which could still be pending.</returns>
+ /// <remarks>This method always completes immediately even if the operation is still pending. The <see cref="IAsyncResult"/> object returned represents the operation
+ /// and must be passed to <see cref="EndWrite"/> to retrieve the result of the operation. For every call to this method a matching call to
+ /// <see cref="EndWrite"/> must be made. When <paramref name="userCallback"/> specifies a callback function, this function will be called when the operation is completed. The optional
+ /// <paramref name="stateObject"/> parameter can be used to pass user-defined information to this callback or the <see cref="IAsyncResult"/>. The <see cref="IAsyncResult"/>
+ /// also provides an event handle (<see cref="IAsyncResult.AsyncWaitHandle" />) that will be triggered when the operation is complete as well.
+ /// </remarks>
+ 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;
+ }
+
+ /// <summary>
+ /// Waits for a pending asynchronous write operation to complete.
+ /// </summary>
+ /// <param name="asyncResult">The <see cref="IAsyncResult"/> object representing the asynchonous operation,
+ /// as returned by <see cref="BeginWrite"/>.</param>
+ /// <returns>The number of bytes transfered during the operation.</returns>
+ /// <remarks>Every call to <see cref="BeginWrite"/> must have a matching call to <see cref="EndWrite"/> 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.</remarks>
+ 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();
+ }
+ }
+
+ /// <summary>
+ /// Aborts all pending transfers for this pipe.
+ /// </summary>
+ public void Abort()
+ {
+ try
+ {
+ _device.InternalDevice.AbortPipe(Interface.InterfaceIndex, _pipeInfo.PipeId);
+ }
+ catch (API.APIException e)
+ {
+ throw new USBException("Failed to abort pipe.", e);
+ }
+ }
+
+ /// <summary>
+ /// Flushes the pipe, discarding any data that is cached. Only available on IN direction pipes.
+ /// </summary>
+ 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
+{
+ /// <summary>
+ /// Collection of UsbPipe objects
+ /// </summary>
+ public class USBPipeCollection : IEnumerable<USBPipe>
+ {
+ private Dictionary<byte, USBPipe> _pipes;
+
+ internal USBPipeCollection(USBPipe[] pipes)
+ {
+ _pipes = new Dictionary<byte, USBPipe>(pipes.Length);
+ foreach (USBPipe pipe in pipes)
+ {
+ if (_pipes.ContainsKey(pipe.Address))
+ throw new USBException("Duplicate pipe address in endpoint.");
+ _pipes[pipe.Address] = pipe;
+ }
+ }
+
+ /// <summary>
+ /// Returns the pipe from the collection with the given pipe address
+ /// </summary>
+ /// <param name="pipeAddress">Address of the pipe to return</param>
+ /// <returns>The pipe with the given pipe address</returns>
+ /// <exception cref="IndexOutOfRangeException">Thrown if no pipe with the specified address
+ /// is available in the collection.</exception>
+ public USBPipe this [byte pipeAddress]
+ {
+ get
+ {
+ USBPipe pipe;
+ if (!_pipes.TryGetValue(pipeAddress, out pipe))
+ throw new IndexOutOfRangeException();
+ return pipe;
+ }
+ }
+
+ private class UsbPipeEnumerator : IEnumerator<USBPipe>
+ {
+ 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;
+ }
+
+ /// <summary>
+ /// Returns a typed enumerator that iterates through a collection.
+ /// </summary>
+ /// <returns>The enumerator object that can be used to iterate through the collection.</returns>
+ public IEnumerator<USBPipe> GetEnumerator()
+ {
+ return new UsbPipeEnumerator(GetPipeList());
+ }
+
+ /// <summary>
+ /// Returns an enumerator that iterates through a collection.
+ /// </summary>
+ /// <returns>An IEnumerator object that can be used to iterate through the collection.</returns>
+ 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
+{
+
+ /// <summary>
+ /// Describes the policy for a specific USB pipe
+ /// </summary>
+ 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.");
+ }
+
+ /// <summary>
+ /// 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.
+ /// </summary>
+ /// <seealso href="http://msdn.microsoft.com/en-us/library/aa476439.aspx">WinUSB_GetPipePolicy for a more detailed description</seealso>
+ 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);
+ }
+ }
+
+ /// <summary>
+ /// When true, the driver fails stalled data transfers, but the driver clears the stall condition automatically. Default
+ /// value is false.
+ /// </summary>
+ /// <seealso href="http://msdn.microsoft.com/en-us/library/aa476439.aspx">WinUSB_GetPipePolicy for a more detailed description</seealso>
+ 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);
+ }
+ }
+
+ /// <summary>
+ /// 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.
+ /// </summary>
+ /// <seealso href="http://msdn.microsoft.com/en-us/library/aa476439.aspx">WinUSB_GetPipePolicy for a more detailed description</seealso>
+ 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);
+ }
+ }
+ /// <summary>
+ /// 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.
+ /// </summary>
+ /// <seealso href="http://msdn.microsoft.com/en-us/library/aa476439.aspx">WinUSB_GetPipePolicy for a more detailed description</seealso>
+ 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);
+ }
+ }
+
+ /// <summary>
+ /// 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.
+ /// </summary>
+ /// <seealso href="http://msdn.microsoft.com/en-us/library/aa476439.aspx">WinUSB_GetPipePolicy for a more detailed description</seealso>
+ 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);
+ }
+ }
+
+ /// <summary>
+ /// 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.
+ /// </summary>
+ /// <seealso href="http://msdn.microsoft.com/en-us/library/aa476439.aspx">WinUSB_GetPipePolicy for a more detailed description</seealso>
+ 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);
+ }
+ }
+
+ /// <summary>
+ /// 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.
+ /// </summary>
+ /// <seealso href="http://msdn.microsoft.com/en-us/library/aa476439.aspx">WinUSB_GetPipePolicy for a more detailed description</seealso>
+ 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);
+ }
+ }
+
+ }
+}