35
|
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 case UsbControlRequestType.TypeVendor: |
|
185 case UsbControlRequestType.TypeClass: |
|
186 USBIO_CLASS_OR_VENDOR_REQUEST req = new USBIO_CLASS_OR_VENDOR_REQUEST() { |
|
187 Flags = USBIO_SHORT_TRANSFER_OK, |
|
188 Type = (USBIO_REQUEST_TYPE)((int)(requestType & UsbControlRequestType.TypeMask) >> 5), |
|
189 Recipient = (USBIO_REQUEST_RECIPIENT)((int)(requestType & UsbControlRequestType.RecipMask) >> 0), |
|
190 RequestTypeReservedBits = 0, |
|
191 Request = request, |
|
192 Value = value, |
|
193 Index = index, |
|
194 }; |
|
195 fixed (Byte* b = buffer) { |
|
196 if ((requestType & UsbControlRequestType.EndpointMask) == UsbControlRequestType.EndpointIn) { |
|
197 return DeviceIoControl(DeviceHandle, IOCTL_USBIO_CLASS_OR_VENDOR_IN_REQUEST, (IntPtr)(&req), sizeof(USBIO_CLASS_OR_VENDOR_REQUEST), (IntPtr)(b + offset), length); |
|
198 } else { |
|
199 return DeviceIoControl(DeviceHandle, IOCTL_USBIO_CLASS_OR_VENDOR_OUT_REQUEST, (IntPtr)(&req), sizeof(USBIO_CLASS_OR_VENDOR_REQUEST), (IntPtr)(b + offset), length); |
|
200 } |
|
201 } |
|
202 case UsbControlRequestType.TypeReserved: |
|
203 default: |
|
204 throw new ArgumentException(String.Format("Invalid or unsupported request type: 0x{0:X8}", requestType)); |
|
205 } |
|
206 } |
|
207 private unsafe SafeFileHandle OpenHandleForPipe(Byte epID) { |
|
208 SafeFileHandle handle = OpenHandle(); |
|
209 USBIO_BIND_PIPE req = new USBIO_BIND_PIPE() { EndpointAddress = epID }; |
|
210 try { |
|
211 DeviceIoControl(handle, IOCTL_USBIO_BIND_PIPE, (IntPtr)(&req), sizeof(USBIO_BIND_PIPE), IntPtr.Zero, 0); |
|
212 } catch (Exception) { |
|
213 handle.Close(); |
|
214 throw; |
|
215 } |
|
216 return handle; |
|
217 } |
|
218 private SafeFileHandle GetHandleForPipe(Byte epID) { |
|
219 int epidx = epID & 0x7F; |
|
220 if ((epID & 0x80) != 0) { |
|
221 if (PipeHandlesIn != null && PipeHandlesIn.Length >= epidx && PipeHandlesIn[epidx] != null) return PipeHandlesIn[epidx]; |
|
222 SafeFileHandle handle = OpenHandleForPipe(epID); |
|
223 if (PipeHandlesIn == null) PipeHandlesIn = new SafeFileHandle[epidx + 1]; |
|
224 else Array.Resize(ref PipeHandlesIn, epidx + 1); |
|
225 PipeHandlesIn[epidx] = handle; |
|
226 return handle; |
|
227 } else { |
|
228 if (PipeHandlesOut != null && PipeHandlesOut.Length >= epidx && PipeHandlesOut[epidx] != null) return PipeHandlesOut[epidx]; |
|
229 SafeFileHandle handle = OpenHandleForPipe(epID); |
|
230 if (PipeHandlesOut == null) PipeHandlesOut = new SafeFileHandle[epidx + 1]; |
|
231 else Array.Resize(ref PipeHandlesOut, epidx + 1); |
|
232 PipeHandlesOut[epidx] = handle; |
|
233 return handle; |
|
234 } |
|
235 } |
|
236 unsafe int PipeRead(Byte epnum, Byte[] buffer, int offset, int length) { |
|
237 if (offset < 0 || length < 0 || offset + length > buffer.Length) throw new ArgumentOutOfRangeException("length", "The specified offset and length exceed the buffer length"); |
|
238 SafeFileHandle handle = GetHandleForPipe(epnum); |
|
239 uint ret; |
|
240 fixed (Byte* b = buffer) { |
|
241 if (!ReadFile(handle, b + offset, (uint)length, out ret, IntPtr.Zero)) throw new Win32Exception(Marshal.GetLastWin32Error()); |
|
242 } |
|
243 return (int)ret; |
|
244 } |
|
245 unsafe int PipeWrite(Byte epnum, Byte[] buffer, int offset, int length) { |
|
246 if (offset < 0 || length < 0 || offset + length > buffer.Length) throw new ArgumentOutOfRangeException("length", "The specified offset and length exceed the buffer length"); |
|
247 SafeFileHandle handle = GetHandleForPipe(epnum); |
|
248 uint ret; |
|
249 fixed (Byte* b = buffer) { |
|
250 if (!WriteFile(handle, b + offset, (uint)length, out ret, IntPtr.Zero)) throw new Win32Exception(Marshal.GetLastWin32Error()); |
|
251 } |
|
252 return (int)ret; |
|
253 } |
|
254 public void PipeReset(byte pipeID) { |
|
255 throw new NotImplementedException(); |
|
256 } |
|
257 |
|
258 private unsafe int DeviceIoControl(SafeHandle hDevice, int IoControlCode, IntPtr InBuffer, int nInBufferSize, IntPtr OutBuffer, int nOutBufferSize) { |
|
259 int pBytesReturned; |
|
260 if (Kernel32.DeviceIoControl(hDevice, IoControlCode, InBuffer, nInBufferSize, OutBuffer, nOutBufferSize, out pBytesReturned, null)) |
|
261 return pBytesReturned; |
|
262 throw new Win32Exception(Marshal.GetLastWin32Error()); |
|
263 } |
|
264 |
|
265 public override UsbPipeStream GetBulkStream(byte endpoint) { |
|
266 return new PipeStream(this, endpoint, false, GetHandleForPipe(endpoint)); |
|
267 } |
|
268 public override UsbPipeStream GetInterruptStream(byte endpoint) { |
|
269 return new PipeStream(this, endpoint, true, GetHandleForPipe(endpoint)); |
|
270 } |
|
271 |
|
272 class PipeStream : UsbPipeStream { |
|
273 private SafeFileHandle Handle; |
|
274 public PipeStream(IUsbInterface device, Byte endpoint, Boolean interrupt, SafeFileHandle handle) : base(device, endpoint, interrupt) { |
|
275 this.Handle = handle; |
|
276 } |
|
277 |
|
278 public unsafe override void Write(byte[] buffer, int offset, int length) { |
|
279 if (offset < 0 || length < 0 || offset + length > buffer.Length) throw new ArgumentOutOfRangeException("length", "The specified offset and length exceed the buffer length"); |
|
280 uint ret; |
|
281 fixed (Byte* b = buffer) { |
|
282 if (!WriteFile(Handle, b + offset, (uint)length, out ret, IntPtr.Zero)) throw new Win32Exception(Marshal.GetLastWin32Error()); |
|
283 } |
|
284 if (ret <= 0) throw new EndOfStreamException("Could not write all data"); |
|
285 } |
|
286 |
|
287 public unsafe override int Read(byte[] buffer, int offset, int length) { |
|
288 if (offset < 0 || length < 0 || offset + length > buffer.Length) throw new ArgumentOutOfRangeException("length", "The specified offset and length exceed the buffer length"); |
|
289 uint ret; |
|
290 fixed (Byte* b = buffer) { |
|
291 if (!WriteFile(Handle, b + offset, (uint)length, out ret, IntPtr.Zero)) throw new Win32Exception(Marshal.GetLastWin32Error()); |
|
292 } |
|
293 return (int)ret; |
|
294 } |
|
295 } |
|
296 } |
|
297 } |