changeset 35:6fcedb1030bf

USBLib: Added support for USBIO driver
author Ivo Smits <Ivo@UCIS.nl>
date Sun, 21 Apr 2013 18:32:04 +0200
parents 70bde4fa6a2f
children c4a5dbe62513
files UCIS.Core.csproj USBLib/Communication/LibUsbDotNet.cs USBLib/Communication/USBIO/USBIODevice.cs USBLib/Communication/USBIO/USBIORegistry.cs USBLib/Communication/UsbInterface.cs
diffstat 5 files changed, 341 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/UCIS.Core.csproj	Sun Apr 21 17:01:03 2013 +0200
+++ b/UCIS.Core.csproj	Sun Apr 21 18:32:04 2013 +0200
@@ -149,6 +149,8 @@
     <Compile Include="USBLib\Communication\UsbControlRequestType.cs" />
     <Compile Include="USBLib\Communication\UsbDescriptorType.cs" />
     <Compile Include="USBLib\Communication\UsbInterface.cs" />
+    <Compile Include="USBLib\Communication\USBIO\USBIODevice.cs" />
+    <Compile Include="USBLib\Communication\USBIO\USBIORegistry.cs" />
     <Compile Include="USBLib\Communication\UsbPipeStream.cs" />
     <Compile Include="USBLib\Communication\WindowsUsbDeviceRegistry.cs" />
     <Compile Include="USBLib\Communication\WinUsb\WinUsbDevice.cs" />
--- a/USBLib/Communication/LibUsbDotNet.cs	Sun Apr 21 17:01:03 2013 +0200
+++ b/USBLib/Communication/LibUsbDotNet.cs	Sun Apr 21 18:32:04 2013 +0200
@@ -14,6 +14,7 @@
 using nIUsbDevice = UCIS.USBLib.Communication.IUsbDevice;
 using nIUsbInterface = UCIS.USBLib.Communication.IUsbInterface;
 using WinUsbRegistry = UCIS.USBLib.Communication.WinUsb.WinUsbRegistry;
+using USBIORegistry = UCIS.USBLib.Communication.USBIO.USBIORegistry;
 
 namespace LibUsbDotNet {
 	public class UsbDevice : IUsbDevice {
@@ -64,6 +65,7 @@
 				if (Environment.OSVersion.Platform == PlatformID.Win32NT) {
 					foreach (IUsbDeviceRegistry reg in WinUsbRegistry.DeviceList) list.Add(new UsbRegistry(reg));
 					foreach (IUsbDeviceRegistry reg in LibUsb0Registry.DeviceList) list.Add(new UsbRegistry(reg));
+					foreach (IUsbDeviceRegistry reg in USBIORegistry.DeviceList) list.Add(new UsbRegistry(reg));
 				} else {
 					foreach (IUsbDeviceRegistry reg in LibUsb1Registry.DeviceList) list.Add(new UsbRegistry(reg));
 				}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USBLib/Communication/USBIO/USBIODevice.cs	Sun Apr 21 18:32:04 2013 +0200
@@ -0,0 +1,298 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.IO;
+using System.Runtime.InteropServices;
+using Microsoft.Win32.SafeHandles;
+using UCIS.USBLib.Internal.Windows;
+
+namespace UCIS.USBLib.Communication.USBIO {
+	public class USBIODevice : UsbInterface, IUsbDevice {
+		public string DeviceFilename { get; private set; }
+		public IUsbDeviceRegistry Registry { get; private set; }
+		private SafeFileHandle DeviceHandle;
+		private SafeFileHandle[] PipeHandlesIn = null;
+		private SafeFileHandle[] PipeHandlesOut = null;
+
+		static int CTL_CODE(int DeviceType, int Function, int Method, int Access) { return ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method); }
+		static int _USBIO_IOCTL_CODE(int FnCode, int Method) { return CTL_CODE(0x8094, 0x800 + FnCode, Method, 0); }
+		const int METHOD_BUFFERED = 0;
+		const int METHOD_IN_DIRECT = 1;
+		const int METHOD_OUT_DIRECT = 2;
+		static readonly int IOCTL_USBIO_GET_DESCRIPTOR = _USBIO_IOCTL_CODE(1, METHOD_OUT_DIRECT);
+		static readonly int IOCTL_USBIO_GET_CONFIGURATION = _USBIO_IOCTL_CODE(6, METHOD_BUFFERED);
+		static readonly int IOCTL_USBIO_SET_CONFIGURATION = _USBIO_IOCTL_CODE(9, METHOD_BUFFERED);
+		static readonly int IOCTL_USBIO_CLASS_OR_VENDOR_IN_REQUEST = _USBIO_IOCTL_CODE(12, METHOD_OUT_DIRECT);
+		static readonly int IOCTL_USBIO_CLASS_OR_VENDOR_OUT_REQUEST = _USBIO_IOCTL_CODE(13, METHOD_IN_DIRECT);
+		static readonly int IOCTL_USBIO_RESET_DEVICE = _USBIO_IOCTL_CODE(21, METHOD_BUFFERED);
+		static readonly int IOCTL_USBIO_BIND_PIPE = _USBIO_IOCTL_CODE(30, METHOD_BUFFERED);
+
+		[DllImport("kernel32.dll", SetLastError = true)]
+		static unsafe extern bool ReadFile(SafeFileHandle hFile, byte* lpBuffer, uint nNumberOfBytesToRead, out uint lpNumberOfBytesRead, IntPtr lpOverlapped);
+		[DllImport("kernel32.dll", SetLastError = true)]
+		static unsafe extern bool WriteFile(SafeFileHandle hFile, byte* lpBuffer, uint nNumberOfBytesToWrite, out uint lpNumberOfBytesWritten, IntPtr lpOverlapped);
+
+		enum USBIO_REQUEST_RECIPIENT : uint {
+			Device = 0,
+			Interface,
+			Endpoint,
+			Other,
+		}
+		enum USBIO_REQUEST_TYPE : uint {
+			Class = 1,
+			Vendor,
+		}
+
+		const UInt32 USBIO_SHORT_TRANSFER_OK = 0x00010000;
+
+		[StructLayout(LayoutKind.Sequential, Pack = 1)]
+		struct USBIO_DESCRIPTOR_REQUEST {
+			public USBIO_REQUEST_RECIPIENT Recipient;
+			public Byte DescriptorType;
+			public Byte DescriptorIndex;
+			public Int16 LanguageId;
+		}
+		[StructLayout(LayoutKind.Sequential, Pack = 1)]
+		struct USBIO_BIND_PIPE {
+			public Byte EndpointAddress;
+		}
+		[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 2 + 2 + 4)]
+		struct USBIO_INTERFACE_SETTING {
+			public UInt16 InterfaceIndex;
+			public UInt16 AlternateSettingIndex;
+			public UInt32 MaximumTransferSize;
+		}
+		[StructLayout(LayoutKind.Sequential, Pack = 1)]
+		unsafe struct USBIO_SET_CONFIGURATION{
+			public UInt16 ConfigurationIndex;
+			public UInt16 NbOfInterfaces;
+			public fixed byte InterfaceList[32 * (2 + 2 + 4)];
+		}
+		[StructLayout(LayoutKind.Sequential, Pack = 1)]
+		struct USBIO_CLASS_OR_VENDOR_REQUEST {
+			public UInt32 Flags;
+			public USBIO_REQUEST_TYPE Type;
+			public USBIO_REQUEST_RECIPIENT Recipient;
+			public Byte RequestTypeReservedBits;
+			public Byte Request;
+			public Int16 Value;
+			public Int16 Index;
+		}
+
+		public USBIODevice(String path, USBIORegistry registry) {
+			DeviceFilename = path;
+			this.Registry = registry;
+			DeviceHandle = OpenHandle();
+		}
+		private SafeFileHandle OpenHandle() {
+			SafeFileHandle handle = Kernel32.CreateFile(DeviceFilename,
+						   NativeFileAccess.FILE_GENERIC_READ | NativeFileAccess.FILE_GENERIC_WRITE,
+						   NativeFileShare.FILE_SHARE_WRITE | NativeFileShare.FILE_SHARE_READ,
+						   IntPtr.Zero,
+						   NativeFileMode.OPEN_EXISTING,
+						   NativeFileFlag.FILE_ATTRIBUTE_NORMAL,
+						   IntPtr.Zero);
+			if (handle.IsInvalid || handle.IsClosed) throw new Win32Exception(Marshal.GetLastWin32Error(), "Could not open device");
+			return handle;
+		}
+		public override void Close() {
+			if (PipeHandlesIn != null) for (int i = 0; i < PipeHandlesIn.Length; i++) if (PipeHandlesIn[i] != null) PipeHandlesIn[i].Close();
+			if (PipeHandlesOut != null) for (int i = 0; i < PipeHandlesOut.Length; i++) if (PipeHandlesOut[i] != null) PipeHandlesOut[i].Close();
+			if (DeviceHandle != null) DeviceHandle.Close();
+		}
+
+		public override Byte Configuration {
+			get { return base.Configuration; }
+			set {
+				if (value == Configuration) return;
+				IList<LibUsbDotNet.Info.UsbConfigInfo> configs = (new LibUsbDotNet.UsbDevice(this)).Configs;
+				for (int i = 0; i < configs.Count; i++) {
+					LibUsbDotNet.Info.UsbConfigInfo config = configs[i];
+					if (config.Descriptor.ConfigID == value) {
+						unsafe {
+							USBIO_SET_CONFIGURATION req = new USBIO_SET_CONFIGURATION();
+							req.ConfigurationIndex = (ushort)i;
+							req.NbOfInterfaces = Math.Min((ushort)32, config.Descriptor.InterfaceCount);
+							for (int j = 0; j < req.NbOfInterfaces; j++) {
+								LibUsbDotNet.Info.UsbInterfaceInfo intf = config.InterfaceInfoList[i];
+								*((USBIO_INTERFACE_SETTING*)(req.InterfaceList + sizeof(USBIO_INTERFACE_SETTING) * i)) =
+									new USBIO_INTERFACE_SETTING() { InterfaceIndex = intf.Descriptor.InterfaceID, AlternateSettingIndex = 0, MaximumTransferSize = UInt16.MaxValue };
+							}
+							DeviceIoControl(DeviceHandle, IOCTL_USBIO_SET_CONFIGURATION, (IntPtr)(&req), sizeof(USBIO_SET_CONFIGURATION), IntPtr.Zero, 0);
+						}
+						return;
+					}
+				}
+				throw new InvalidOperationException("Requested configuration ID not found");
+			}
+		}
+
+		public void ClaimInterface(int interfaceID) {
+		}
+		public void ReleaseInterface(int interfaceID) {
+		}
+		public void SetAltInterface(int interfaceID, int alternateID) {
+			throw new NotImplementedException();
+		}
+		public void ResetDevice() {
+			DeviceIoControl(DeviceHandle, IOCTL_USBIO_RESET_DEVICE, IntPtr.Zero, 0, IntPtr.Zero, 0);
+		}
+		public unsafe override int GetDescriptor(byte descriptorType, byte index, short langId, byte[] buffer, int offset, int length) {
+			if (offset < 0 || length < 0 || offset + length > buffer.Length) throw new ArgumentOutOfRangeException("length", "The specified offset and length exceed the buffer length");
+			USBIO_DESCRIPTOR_REQUEST req = new USBIO_DESCRIPTOR_REQUEST() { DescriptorType = descriptorType, DescriptorIndex = index, LanguageId = langId, Recipient = USBIO_REQUEST_RECIPIENT.Device };
+			fixed (Byte* b = buffer) {
+				return DeviceIoControl(DeviceHandle, IOCTL_USBIO_GET_DESCRIPTOR, (IntPtr)(&req), sizeof(USBIO_DESCRIPTOR_REQUEST), (IntPtr)(b + offset), length);
+			}
+		}
+		public override int BulkRead(byte endpoint, byte[] buffer, int offset, int length) {
+			return PipeRead(endpoint, buffer, offset, length);
+		}
+		public override int BulkWrite(byte endpoint, byte[] buffer, int offset, int length) {
+			return PipeWrite(endpoint, buffer, offset, length);
+		}
+		public override int InterruptRead(byte endpoint, byte[] buffer, int offset, int length) {
+			return PipeRead(endpoint, buffer, offset, length);
+		}
+		public override int InterruptWrite(byte endpoint, byte[] buffer, int offset, int length) {
+			return PipeWrite(endpoint, buffer, offset, length);
+		}
+		public unsafe override int ControlRead(UsbControlRequestType requestType, byte request, short value, short index, byte[] buffer, int offset, int length) {
+			return ControlTransfer(requestType, request, value, index, buffer, offset, length);
+		}
+		public override int ControlWrite(UsbControlRequestType requestType, byte request, short value, short index, byte[] buffer, int offset, int length) {
+			return ControlTransfer(requestType, request, value, index, buffer, offset, length);
+		}
+		private unsafe int ControlTransfer(UsbControlRequestType requestType, byte request, short value, short index, byte[] buffer, int offset, int length) {
+			if (buffer == null) {
+				if (offset != 0 || length != 0) throw new ArgumentOutOfRangeException("length", "The specified offset and length exceed the buffer length");
+			} else {
+				if (offset < 0 || length < 0 || offset + length > buffer.Length) throw new ArgumentOutOfRangeException("length", "The specified offset and length exceed the buffer length");
+			}
+			switch (requestType & UsbControlRequestType.TypeMask) {
+				case UsbControlRequestType.TypeStandard:
+					switch ((UsbStandardRequest)request) {
+						case UsbStandardRequest.GetDescriptor:
+							return GetDescriptor((Byte)(value >> 8), (Byte)value, index, buffer, offset, length);
+						case UsbStandardRequest.GetConfiguration:
+							fixed (Byte* b = buffer) return DeviceIoControl(DeviceHandle, IOCTL_USBIO_GET_CONFIGURATION, IntPtr.Zero, 0, (IntPtr)(b + offset), length);
+						case UsbStandardRequest.SetConfiguration:
+							Configuration = (Byte)value;
+							return 0;
+						default:
+							throw new ArgumentException(String.Format("Invalid request: 0x{0:X8}", request));
+					}
+					break;
+				case UsbControlRequestType.TypeVendor:
+				case UsbControlRequestType.TypeClass:
+					USBIO_CLASS_OR_VENDOR_REQUEST req = new USBIO_CLASS_OR_VENDOR_REQUEST() {
+						Flags = USBIO_SHORT_TRANSFER_OK,
+						Type = (USBIO_REQUEST_TYPE)((int)(requestType & UsbControlRequestType.TypeMask) >> 5),
+						Recipient = (USBIO_REQUEST_RECIPIENT)((int)(requestType & UsbControlRequestType.RecipMask) >> 0),
+						RequestTypeReservedBits = 0,
+						Request = request,
+						Value = value,
+						Index = index,
+					};
+					fixed (Byte* b = buffer) {
+						if ((requestType & UsbControlRequestType.EndpointMask) == UsbControlRequestType.EndpointIn) {
+							return DeviceIoControl(DeviceHandle, IOCTL_USBIO_CLASS_OR_VENDOR_IN_REQUEST, (IntPtr)(&req), sizeof(USBIO_CLASS_OR_VENDOR_REQUEST), (IntPtr)(b + offset), length);
+						} else {
+							return DeviceIoControl(DeviceHandle, IOCTL_USBIO_CLASS_OR_VENDOR_OUT_REQUEST, (IntPtr)(&req), sizeof(USBIO_CLASS_OR_VENDOR_REQUEST), (IntPtr)(b + offset), length);
+						}
+					}
+				case UsbControlRequestType.TypeReserved:
+				default:
+					throw new ArgumentException(String.Format("Invalid or unsupported request type: 0x{0:X8}", requestType));
+			}
+		}
+		private unsafe SafeFileHandle OpenHandleForPipe(Byte epID) {
+			SafeFileHandle handle = OpenHandle();
+			USBIO_BIND_PIPE req = new USBIO_BIND_PIPE() { EndpointAddress = epID };
+			try {
+				DeviceIoControl(handle, IOCTL_USBIO_BIND_PIPE, (IntPtr)(&req), sizeof(USBIO_BIND_PIPE), IntPtr.Zero, 0);
+			} catch (Exception) {
+				handle.Close();
+				throw;
+			}
+			return handle;
+		}
+		private SafeFileHandle GetHandleForPipe(Byte epID) {
+			int epidx = epID & 0x7F;
+			if ((epID & 0x80) != 0) {
+				if (PipeHandlesIn != null && PipeHandlesIn.Length >= epidx && PipeHandlesIn[epidx] != null) return PipeHandlesIn[epidx];
+				SafeFileHandle handle = OpenHandleForPipe(epID);
+				if (PipeHandlesIn == null) PipeHandlesIn = new SafeFileHandle[epidx + 1];
+				else Array.Resize(ref PipeHandlesIn, epidx + 1);
+				PipeHandlesIn[epidx] = handle;
+				return handle;
+			} else {
+				if (PipeHandlesOut != null && PipeHandlesOut.Length >= epidx && PipeHandlesOut[epidx] != null) return PipeHandlesOut[epidx];
+				SafeFileHandle handle = OpenHandleForPipe(epID);
+				if (PipeHandlesOut == null) PipeHandlesOut = new SafeFileHandle[epidx + 1];
+				else Array.Resize(ref PipeHandlesOut, epidx + 1);
+				PipeHandlesOut[epidx] = handle;
+				return handle;
+			}
+		}
+		unsafe int PipeRead(Byte epnum, Byte[] buffer, int offset, int length) {
+			if (offset < 0 || length < 0 || offset + length > buffer.Length) throw new ArgumentOutOfRangeException("length", "The specified offset and length exceed the buffer length");
+			SafeFileHandle handle = GetHandleForPipe(epnum);
+			uint ret;
+			fixed (Byte* b = buffer) {
+				if (!ReadFile(handle, b + offset, (uint)length, out ret, IntPtr.Zero)) throw new Win32Exception(Marshal.GetLastWin32Error());
+			}
+			return (int)ret;
+		}
+		unsafe int PipeWrite(Byte epnum, Byte[] buffer, int offset, int length) {
+			if (offset < 0 || length < 0 || offset + length > buffer.Length) throw new ArgumentOutOfRangeException("length", "The specified offset and length exceed the buffer length");
+			SafeFileHandle handle = GetHandleForPipe(epnum);
+			uint ret;
+			fixed (Byte* b = buffer) {
+				if (!WriteFile(handle, b + offset, (uint)length, out ret, IntPtr.Zero)) throw new Win32Exception(Marshal.GetLastWin32Error());
+			}
+			return (int)ret;
+		}
+		public void PipeReset(byte pipeID) {
+			throw new NotImplementedException();
+		}
+
+		private unsafe int DeviceIoControl(SafeHandle hDevice, int IoControlCode, IntPtr InBuffer, int nInBufferSize, IntPtr OutBuffer, int nOutBufferSize) {
+			int pBytesReturned;
+			if (Kernel32.DeviceIoControl(hDevice, IoControlCode, InBuffer, nInBufferSize, OutBuffer, nOutBufferSize, out pBytesReturned, null))
+				return pBytesReturned;
+			throw new Win32Exception(Marshal.GetLastWin32Error());
+		}
+
+		public override UsbPipeStream GetBulkStream(byte endpoint) {
+			return new PipeStream(this, endpoint, false, GetHandleForPipe(endpoint));
+		}
+		public override UsbPipeStream GetInterruptStream(byte endpoint) {
+			return new PipeStream(this, endpoint, true, GetHandleForPipe(endpoint));
+		}
+
+		class PipeStream : UsbPipeStream {
+			private SafeFileHandle Handle;
+			public PipeStream(IUsbInterface device, Byte endpoint, Boolean interrupt, SafeFileHandle handle) : base(device, endpoint, interrupt) {
+				this.Handle = handle;
+			}
+
+			public unsafe override void Write(byte[] buffer, int offset, int length) {
+				if (offset < 0 || length < 0 || offset + length > buffer.Length) throw new ArgumentOutOfRangeException("length", "The specified offset and length exceed the buffer length");
+				uint ret;
+				fixed (Byte* b = buffer) {
+					if (!WriteFile(Handle, b + offset, (uint)length, out ret, IntPtr.Zero)) throw new Win32Exception(Marshal.GetLastWin32Error());
+				}
+				if (ret <= 0) throw new EndOfStreamException("Could not write all data");
+			}
+
+			public unsafe override int Read(byte[] buffer, int offset, int length) {
+				if (offset < 0 || length < 0 || offset + length > buffer.Length) throw new ArgumentOutOfRangeException("length", "The specified offset and length exceed the buffer length");
+				uint ret;
+				fixed (Byte* b = buffer) {
+					if (!WriteFile(Handle, b + offset, (uint)length, out ret, IntPtr.Zero)) throw new Win32Exception(Marshal.GetLastWin32Error());
+				}
+				return (int)ret;
+			}
+		}
+	}
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USBLib/Communication/USBIO/USBIORegistry.cs	Sun Apr 21 18:32:04 2013 +0200
@@ -0,0 +1,37 @@
+using System;
+using System.Collections.Generic;
+using UCIS.HWLib.Windows.Devices;
+
+namespace UCIS.USBLib.Communication.USBIO {
+	public class USBIORegistry : WindowsUsbDeviceRegistry, IUsbDeviceRegistry {
+		public static readonly Guid USBIO_IID = new Guid("{325ddf96-938c-11d3-9e34-0080c82727f4}");
+		private USBIORegistry(DeviceNode device, String interfacepath) : base(device, interfacepath) { }
+		public static List<USBIORegistry> GetDevicesByInterfaceClass(Guid classGuid) {
+			List<USBIORegistry> deviceList = new List<USBIORegistry>();
+			IList<DeviceNode> usbdevices = DeviceNode.GetDevices(classGuid);
+			foreach (DeviceNode device in usbdevices) {
+				USBIORegistry regInfo = GetDeviceForDeviceNode(device, classGuid);
+				if (regInfo != null) deviceList.Add(regInfo);
+			}
+			return deviceList;
+		}
+		public static List<USBIORegistry> DeviceList {
+			get { return GetDevicesByInterfaceClass(USBIO_IID); }
+		}
+		public static USBIORegistry GetDeviceForDeviceNode(DeviceNode device, Guid classGuid) {
+			String[] iLibUsb = device.GetInterfaces(classGuid);
+			if (iLibUsb == null || iLibUsb.Length == 0) return null;
+			return new USBIORegistry(device, iLibUsb[0]);
+		}
+		public static USBIORegistry GetDeviceForDeviceNode(DeviceNode device) {
+			return GetDeviceForDeviceNode(device, USBIO_IID);
+		}
+
+		public IUsbDevice Open() {
+			return new USBIODevice(DevicePath, this);
+		}
+		IUsbDevice IUsbDeviceRegistry.Open() {
+			return Open();
+		}
+	}
+}
\ No newline at end of file
--- a/USBLib/Communication/UsbInterface.cs	Sun Apr 21 17:01:03 2013 +0200
+++ b/USBLib/Communication/UsbInterface.cs	Sun Apr 21 18:32:04 2013 +0200
@@ -43,10 +43,10 @@
 		public abstract int ControlWrite(UsbControlRequestType requestType, byte request, short value, short index, byte[] buffer, int offset, int length);
 		public abstract int ControlRead(UsbControlRequestType requestType, byte request, short value, short index, byte[] buffer, int offset, int length);
 
-		public UsbPipeStream GetBulkStream(Byte endpoint) {
+		public virtual UsbPipeStream GetBulkStream(Byte endpoint) {
 			return new UsbPipeStream(this, endpoint, false);
 		}
-		public UsbPipeStream GetInterruptStream(Byte endpoint) {
+		public virtual UsbPipeStream GetInterruptStream(Byte endpoint) {
 			return new UsbPipeStream(this, endpoint, true);
 		}