comparison USBLib/Communication/USBIO/USBIODevice.cs @ 35:6fcedb1030bf

USBLib: Added support for USBIO driver
author Ivo Smits <Ivo@UCIS.nl>
date Sun, 21 Apr 2013 18:32:04 +0200
parents
children 2e3f57f326b3
comparison
equal deleted inserted replaced
34:70bde4fa6a2f 35:6fcedb1030bf
1 using System;
2 using System.Collections.Generic;
3 using System.ComponentModel;
4 using System.IO;
5 using System.Runtime.InteropServices;
6 using Microsoft.Win32.SafeHandles;
7 using UCIS.USBLib.Internal.Windows;
8
9 namespace UCIS.USBLib.Communication.USBIO {
10 public class USBIODevice : UsbInterface, IUsbDevice {
11 public string DeviceFilename { get; private set; }
12 public IUsbDeviceRegistry Registry { get; private set; }
13 private SafeFileHandle DeviceHandle;
14 private SafeFileHandle[] PipeHandlesIn = null;
15 private SafeFileHandle[] PipeHandlesOut = null;
16
17 static int CTL_CODE(int DeviceType, int Function, int Method, int Access) { return ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method); }
18 static int _USBIO_IOCTL_CODE(int FnCode, int Method) { return CTL_CODE(0x8094, 0x800 + FnCode, Method, 0); }
19 const int METHOD_BUFFERED = 0;
20 const int METHOD_IN_DIRECT = 1;
21 const int METHOD_OUT_DIRECT = 2;
22 static readonly int IOCTL_USBIO_GET_DESCRIPTOR = _USBIO_IOCTL_CODE(1, METHOD_OUT_DIRECT);
23 static readonly int IOCTL_USBIO_GET_CONFIGURATION = _USBIO_IOCTL_CODE(6, METHOD_BUFFERED);
24 static readonly int IOCTL_USBIO_SET_CONFIGURATION = _USBIO_IOCTL_CODE(9, METHOD_BUFFERED);
25 static readonly int IOCTL_USBIO_CLASS_OR_VENDOR_IN_REQUEST = _USBIO_IOCTL_CODE(12, METHOD_OUT_DIRECT);
26 static readonly int IOCTL_USBIO_CLASS_OR_VENDOR_OUT_REQUEST = _USBIO_IOCTL_CODE(13, METHOD_IN_DIRECT);
27 static readonly int IOCTL_USBIO_RESET_DEVICE = _USBIO_IOCTL_CODE(21, METHOD_BUFFERED);
28 static readonly int IOCTL_USBIO_BIND_PIPE = _USBIO_IOCTL_CODE(30, METHOD_BUFFERED);
29
30 [DllImport("kernel32.dll", SetLastError = true)]
31 static unsafe extern bool ReadFile(SafeFileHandle hFile, byte* lpBuffer, uint nNumberOfBytesToRead, out uint lpNumberOfBytesRead, IntPtr lpOverlapped);
32 [DllImport("kernel32.dll", SetLastError = true)]
33 static unsafe extern bool WriteFile(SafeFileHandle hFile, byte* lpBuffer, uint nNumberOfBytesToWrite, out uint lpNumberOfBytesWritten, IntPtr lpOverlapped);
34
35 enum USBIO_REQUEST_RECIPIENT : uint {
36 Device = 0,
37 Interface,
38 Endpoint,
39 Other,
40 }
41 enum USBIO_REQUEST_TYPE : uint {
42 Class = 1,
43 Vendor,
44 }
45
46 const UInt32 USBIO_SHORT_TRANSFER_OK = 0x00010000;
47
48 [StructLayout(LayoutKind.Sequential, Pack = 1)]
49 struct USBIO_DESCRIPTOR_REQUEST {
50 public USBIO_REQUEST_RECIPIENT Recipient;
51 public Byte DescriptorType;
52 public Byte DescriptorIndex;
53 public Int16 LanguageId;
54 }
55 [StructLayout(LayoutKind.Sequential, Pack = 1)]
56 struct USBIO_BIND_PIPE {
57 public Byte EndpointAddress;
58 }
59 [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 2 + 2 + 4)]
60 struct USBIO_INTERFACE_SETTING {
61 public UInt16 InterfaceIndex;
62 public UInt16 AlternateSettingIndex;
63 public UInt32 MaximumTransferSize;
64 }
65 [StructLayout(LayoutKind.Sequential, Pack = 1)]
66 unsafe struct USBIO_SET_CONFIGURATION{
67 public UInt16 ConfigurationIndex;
68 public UInt16 NbOfInterfaces;
69 public fixed byte InterfaceList[32 * (2 + 2 + 4)];
70 }
71 [StructLayout(LayoutKind.Sequential, Pack = 1)]
72 struct USBIO_CLASS_OR_VENDOR_REQUEST {
73 public UInt32 Flags;
74 public USBIO_REQUEST_TYPE Type;
75 public USBIO_REQUEST_RECIPIENT Recipient;
76 public Byte RequestTypeReservedBits;
77 public Byte Request;
78 public Int16 Value;
79 public Int16 Index;
80 }
81
82 public USBIODevice(String path, USBIORegistry registry) {
83 DeviceFilename = path;
84 this.Registry = registry;
85 DeviceHandle = OpenHandle();
86 }
87 private SafeFileHandle OpenHandle() {
88 SafeFileHandle handle = Kernel32.CreateFile(DeviceFilename,
89 NativeFileAccess.FILE_GENERIC_READ | NativeFileAccess.FILE_GENERIC_WRITE,
90 NativeFileShare.FILE_SHARE_WRITE | NativeFileShare.FILE_SHARE_READ,
91 IntPtr.Zero,
92 NativeFileMode.OPEN_EXISTING,
93 NativeFileFlag.FILE_ATTRIBUTE_NORMAL,
94 IntPtr.Zero);
95 if (handle.IsInvalid || handle.IsClosed) throw new Win32Exception(Marshal.GetLastWin32Error(), "Could not open device");
96 return handle;
97 }
98 public override void Close() {
99 if (PipeHandlesIn != null) for (int i = 0; i < PipeHandlesIn.Length; i++) if (PipeHandlesIn[i] != null) PipeHandlesIn[i].Close();
100 if (PipeHandlesOut != null) for (int i = 0; i < PipeHandlesOut.Length; i++) if (PipeHandlesOut[i] != null) PipeHandlesOut[i].Close();
101 if (DeviceHandle != null) DeviceHandle.Close();
102 }
103
104 public override Byte Configuration {
105 get { return base.Configuration; }
106 set {
107 if (value == Configuration) return;
108 IList<LibUsbDotNet.Info.UsbConfigInfo> configs = (new LibUsbDotNet.UsbDevice(this)).Configs;
109 for (int i = 0; i < configs.Count; i++) {
110 LibUsbDotNet.Info.UsbConfigInfo config = configs[i];
111 if (config.Descriptor.ConfigID == value) {
112 unsafe {
113 USBIO_SET_CONFIGURATION req = new USBIO_SET_CONFIGURATION();
114 req.ConfigurationIndex = (ushort)i;
115 req.NbOfInterfaces = Math.Min((ushort)32, config.Descriptor.InterfaceCount);
116 for (int j = 0; j < req.NbOfInterfaces; j++) {
117 LibUsbDotNet.Info.UsbInterfaceInfo intf = config.InterfaceInfoList[i];
118 *((USBIO_INTERFACE_SETTING*)(req.InterfaceList + sizeof(USBIO_INTERFACE_SETTING) * i)) =
119 new USBIO_INTERFACE_SETTING() { InterfaceIndex = intf.Descriptor.InterfaceID, AlternateSettingIndex = 0, MaximumTransferSize = UInt16.MaxValue };
120 }
121 DeviceIoControl(DeviceHandle, IOCTL_USBIO_SET_CONFIGURATION, (IntPtr)(&req), sizeof(USBIO_SET_CONFIGURATION), IntPtr.Zero, 0);
122 }
123 return;
124 }
125 }
126 throw new InvalidOperationException("Requested configuration ID not found");
127 }
128 }
129
130 public void ClaimInterface(int interfaceID) {
131 }
132 public void ReleaseInterface(int interfaceID) {
133 }
134 public void SetAltInterface(int interfaceID, int alternateID) {
135 throw new NotImplementedException();
136 }
137 public void ResetDevice() {
138 DeviceIoControl(DeviceHandle, IOCTL_USBIO_RESET_DEVICE, IntPtr.Zero, 0, IntPtr.Zero, 0);
139 }
140 public unsafe override int GetDescriptor(byte descriptorType, byte index, short langId, byte[] buffer, int offset, int length) {
141 if (offset < 0 || length < 0 || offset + length > buffer.Length) throw new ArgumentOutOfRangeException("length", "The specified offset and length exceed the buffer length");
142 USBIO_DESCRIPTOR_REQUEST req = new USBIO_DESCRIPTOR_REQUEST() { DescriptorType = descriptorType, DescriptorIndex = index, LanguageId = langId, Recipient = USBIO_REQUEST_RECIPIENT.Device };
143 fixed (Byte* b = buffer) {
144 return DeviceIoControl(DeviceHandle, IOCTL_USBIO_GET_DESCRIPTOR, (IntPtr)(&req), sizeof(USBIO_DESCRIPTOR_REQUEST), (IntPtr)(b + offset), length);
145 }
146 }
147 public override int BulkRead(byte endpoint, byte[] buffer, int offset, int length) {
148 return PipeRead(endpoint, buffer, offset, length);
149 }
150 public override int BulkWrite(byte endpoint, byte[] buffer, int offset, int length) {
151 return PipeWrite(endpoint, buffer, offset, length);
152 }
153 public override int InterruptRead(byte endpoint, byte[] buffer, int offset, int length) {
154 return PipeRead(endpoint, buffer, offset, length);
155 }
156 public override int InterruptWrite(byte endpoint, byte[] buffer, int offset, int length) {
157 return PipeWrite(endpoint, buffer, offset, length);
158 }
159 public unsafe override int ControlRead(UsbControlRequestType requestType, byte request, short value, short index, byte[] buffer, int offset, int length) {
160 return ControlTransfer(requestType, request, value, index, buffer, offset, length);
161 }
162 public override int ControlWrite(UsbControlRequestType requestType, byte request, short value, short index, byte[] buffer, int offset, int length) {
163 return ControlTransfer(requestType, request, value, index, buffer, offset, length);
164 }
165 private unsafe int ControlTransfer(UsbControlRequestType requestType, byte request, short value, short index, byte[] buffer, int offset, int length) {
166 if (buffer == null) {
167 if (offset != 0 || length != 0) throw new ArgumentOutOfRangeException("length", "The specified offset and length exceed the buffer length");
168 } else {
169 if (offset < 0 || length < 0 || offset + length > buffer.Length) throw new ArgumentOutOfRangeException("length", "The specified offset and length exceed the buffer length");
170 }
171 switch (requestType & UsbControlRequestType.TypeMask) {
172 case UsbControlRequestType.TypeStandard:
173 switch ((UsbStandardRequest)request) {
174 case UsbStandardRequest.GetDescriptor:
175 return GetDescriptor((Byte)(value >> 8), (Byte)value, index, buffer, offset, length);
176 case UsbStandardRequest.GetConfiguration:
177 fixed (Byte* b = buffer) return DeviceIoControl(DeviceHandle, IOCTL_USBIO_GET_CONFIGURATION, IntPtr.Zero, 0, (IntPtr)(b + offset), length);
178 case UsbStandardRequest.SetConfiguration:
179 Configuration = (Byte)value;
180 return 0;
181 default:
182 throw new ArgumentException(String.Format("Invalid request: 0x{0:X8}", request));
183 }
184 break;
185 case UsbControlRequestType.TypeVendor:
186 case UsbControlRequestType.TypeClass:
187 USBIO_CLASS_OR_VENDOR_REQUEST req = new USBIO_CLASS_OR_VENDOR_REQUEST() {
188 Flags = USBIO_SHORT_TRANSFER_OK,
189 Type = (USBIO_REQUEST_TYPE)((int)(requestType & UsbControlRequestType.TypeMask) >> 5),
190 Recipient = (USBIO_REQUEST_RECIPIENT)((int)(requestType & UsbControlRequestType.RecipMask) >> 0),
191 RequestTypeReservedBits = 0,
192 Request = request,
193 Value = value,
194 Index = index,
195 };
196 fixed (Byte* b = buffer) {
197 if ((requestType & UsbControlRequestType.EndpointMask) == UsbControlRequestType.EndpointIn) {
198 return DeviceIoControl(DeviceHandle, IOCTL_USBIO_CLASS_OR_VENDOR_IN_REQUEST, (IntPtr)(&req), sizeof(USBIO_CLASS_OR_VENDOR_REQUEST), (IntPtr)(b + offset), length);
199 } else {
200 return DeviceIoControl(DeviceHandle, IOCTL_USBIO_CLASS_OR_VENDOR_OUT_REQUEST, (IntPtr)(&req), sizeof(USBIO_CLASS_OR_VENDOR_REQUEST), (IntPtr)(b + offset), length);
201 }
202 }
203 case UsbControlRequestType.TypeReserved:
204 default:
205 throw new ArgumentException(String.Format("Invalid or unsupported request type: 0x{0:X8}", requestType));
206 }
207 }
208 private unsafe SafeFileHandle OpenHandleForPipe(Byte epID) {
209 SafeFileHandle handle = OpenHandle();
210 USBIO_BIND_PIPE req = new USBIO_BIND_PIPE() { EndpointAddress = epID };
211 try {
212 DeviceIoControl(handle, IOCTL_USBIO_BIND_PIPE, (IntPtr)(&req), sizeof(USBIO_BIND_PIPE), IntPtr.Zero, 0);
213 } catch (Exception) {
214 handle.Close();
215 throw;
216 }
217 return handle;
218 }
219 private SafeFileHandle GetHandleForPipe(Byte epID) {
220 int epidx = epID & 0x7F;
221 if ((epID & 0x80) != 0) {
222 if (PipeHandlesIn != null && PipeHandlesIn.Length >= epidx && PipeHandlesIn[epidx] != null) return PipeHandlesIn[epidx];
223 SafeFileHandle handle = OpenHandleForPipe(epID);
224 if (PipeHandlesIn == null) PipeHandlesIn = new SafeFileHandle[epidx + 1];
225 else Array.Resize(ref PipeHandlesIn, epidx + 1);
226 PipeHandlesIn[epidx] = handle;
227 return handle;
228 } else {
229 if (PipeHandlesOut != null && PipeHandlesOut.Length >= epidx && PipeHandlesOut[epidx] != null) return PipeHandlesOut[epidx];
230 SafeFileHandle handle = OpenHandleForPipe(epID);
231 if (PipeHandlesOut == null) PipeHandlesOut = new SafeFileHandle[epidx + 1];
232 else Array.Resize(ref PipeHandlesOut, epidx + 1);
233 PipeHandlesOut[epidx] = handle;
234 return handle;
235 }
236 }
237 unsafe int PipeRead(Byte epnum, Byte[] buffer, int offset, int length) {
238 if (offset < 0 || length < 0 || offset + length > buffer.Length) throw new ArgumentOutOfRangeException("length", "The specified offset and length exceed the buffer length");
239 SafeFileHandle handle = GetHandleForPipe(epnum);
240 uint ret;
241 fixed (Byte* b = buffer) {
242 if (!ReadFile(handle, b + offset, (uint)length, out ret, IntPtr.Zero)) throw new Win32Exception(Marshal.GetLastWin32Error());
243 }
244 return (int)ret;
245 }
246 unsafe int PipeWrite(Byte epnum, Byte[] buffer, int offset, int length) {
247 if (offset < 0 || length < 0 || offset + length > buffer.Length) throw new ArgumentOutOfRangeException("length", "The specified offset and length exceed the buffer length");
248 SafeFileHandle handle = GetHandleForPipe(epnum);
249 uint ret;
250 fixed (Byte* b = buffer) {
251 if (!WriteFile(handle, b + offset, (uint)length, out ret, IntPtr.Zero)) throw new Win32Exception(Marshal.GetLastWin32Error());
252 }
253 return (int)ret;
254 }
255 public void PipeReset(byte pipeID) {
256 throw new NotImplementedException();
257 }
258
259 private unsafe int DeviceIoControl(SafeHandle hDevice, int IoControlCode, IntPtr InBuffer, int nInBufferSize, IntPtr OutBuffer, int nOutBufferSize) {
260 int pBytesReturned;
261 if (Kernel32.DeviceIoControl(hDevice, IoControlCode, InBuffer, nInBufferSize, OutBuffer, nOutBufferSize, out pBytesReturned, null))
262 return pBytesReturned;
263 throw new Win32Exception(Marshal.GetLastWin32Error());
264 }
265
266 public override UsbPipeStream GetBulkStream(byte endpoint) {
267 return new PipeStream(this, endpoint, false, GetHandleForPipe(endpoint));
268 }
269 public override UsbPipeStream GetInterruptStream(byte endpoint) {
270 return new PipeStream(this, endpoint, true, GetHandleForPipe(endpoint));
271 }
272
273 class PipeStream : UsbPipeStream {
274 private SafeFileHandle Handle;
275 public PipeStream(IUsbInterface device, Byte endpoint, Boolean interrupt, SafeFileHandle handle) : base(device, endpoint, interrupt) {
276 this.Handle = handle;
277 }
278
279 public unsafe override void Write(byte[] buffer, int offset, int length) {
280 if (offset < 0 || length < 0 || offset + length > buffer.Length) throw new ArgumentOutOfRangeException("length", "The specified offset and length exceed the buffer length");
281 uint ret;
282 fixed (Byte* b = buffer) {
283 if (!WriteFile(Handle, b + offset, (uint)length, out ret, IntPtr.Zero)) throw new Win32Exception(Marshal.GetLastWin32Error());
284 }
285 if (ret <= 0) throw new EndOfStreamException("Could not write all data");
286 }
287
288 public unsafe override int Read(byte[] buffer, int offset, int length) {
289 if (offset < 0 || length < 0 || offset + length > buffer.Length) throw new ArgumentOutOfRangeException("length", "The specified offset and length exceed the buffer length");
290 uint ret;
291 fixed (Byte* b = buffer) {
292 if (!WriteFile(Handle, b + offset, (uint)length, out ret, IntPtr.Zero)) throw new Win32Exception(Marshal.GetLastWin32Error());
293 }
294 return (int)ret;
295 }
296 }
297 }
298 }