21
|
1 using System; |
|
2 using System.ComponentModel; |
|
3 using System.Runtime.InteropServices; |
|
4 using System.Threading; |
|
5 using Microsoft.Win32.SafeHandles; |
|
6 using UCIS.USBLib.Internal.Windows; |
|
7 |
|
8 namespace UCIS.USBLib.Communication.LibUsb { |
|
9 public class LibUsb0Device : UsbInterface, IUsbDevice { |
|
10 //private readonly List<int> mClaimedInterfaces = new List<int>(); |
|
11 public string DeviceFilename { get; private set; } |
|
12 public IUsbDeviceRegistry Registry { get; private set; } |
|
13 private SafeFileHandle DeviceHandle; |
|
14 |
|
15 public LibUsb0Device(String path, LibUsb0Registry registry) { |
|
16 DeviceFilename = path; |
|
17 this.Registry = registry; |
|
18 DeviceHandle = Kernel32.CreateFile(DeviceFilename, |
|
19 NativeFileAccess.SPECIAL, |
|
20 NativeFileShare.NONE, |
|
21 IntPtr.Zero, |
|
22 NativeFileMode.OPEN_EXISTING, |
|
23 NativeFileFlag.FILE_FLAG_OVERLAPPED, |
|
24 IntPtr.Zero); |
|
25 if (DeviceHandle.IsInvalid || DeviceHandle.IsClosed) throw new Win32Exception(Marshal.GetLastWin32Error(), "Could not open device"); |
|
26 } |
|
27 public override void Close() { |
|
28 if (DeviceHandle != null) DeviceHandle.Close(); |
|
29 } |
|
30 |
|
31 public override Byte Configuration { |
|
32 get { return base.Configuration; } |
|
33 set { |
|
34 ControlWrite( |
|
35 UsbControlRequestType.EndpointOut | UsbControlRequestType.TypeStandard | UsbControlRequestType.RecipDevice, |
|
36 (byte)UsbStandardRequest.SetConfiguration, |
|
37 value, |
|
38 0, null, 0, 0); |
|
39 } |
|
40 } |
|
41 |
|
42 public void ClaimInterface(int interfaceID) { |
|
43 LibUsbRequest req = new LibUsbRequest(); |
|
44 req.Iface.ID = interfaceID; |
|
45 req.Timeout = UsbConstants.DEFAULT_TIMEOUT; |
|
46 int ret; |
|
47 DeviceIoControl(DeviceHandle, LibUsbIoCtl.CLAIM_INTERFACE, ref req, LibUsbRequest.Size, null, 0, out ret); |
|
48 } |
|
49 public void ReleaseInterface(int interfaceID) { |
|
50 LibUsbRequest req = new LibUsbRequest(); |
|
51 req.Iface.ID = interfaceID; |
|
52 req.Timeout = UsbConstants.DEFAULT_TIMEOUT; |
|
53 int ret; |
|
54 DeviceIoControl(DeviceHandle, LibUsbIoCtl.RELEASE_INTERFACE, ref req, LibUsbRequest.Size, null, 0, out ret); |
|
55 } |
|
56 public void SetAltInterface(int interfaceID, int alternateID) { |
|
57 LibUsbRequest req = new LibUsbRequest(); |
|
58 req.Iface.ID = interfaceID; |
|
59 req.Iface.AlternateID = alternateID; |
|
60 req.Timeout = UsbConstants.DEFAULT_TIMEOUT; |
|
61 int ret; |
|
62 DeviceIoControl(DeviceHandle, LibUsbIoCtl.SET_INTERFACE, ref req, LibUsbRequest.Size, null, 0, out ret); |
|
63 } |
|
64 public void ResetDevice() { |
|
65 LibUsbRequest req = new LibUsbRequest(); |
|
66 req.Timeout = UsbConstants.DEFAULT_TIMEOUT; |
|
67 int ret; |
|
68 DeviceIoControl(DeviceHandle, LibUsbIoCtl.RESET_DEVICE, ref req, LibUsbRequest.Size, null, 0, out ret); |
|
69 } |
|
70 public unsafe override int GetDescriptor(byte descriptorType, byte index, short langId, byte[] buffer, int offset, int length) { |
|
71 if (offset < 0 || length < 0 || offset + length > buffer.Length) throw new ArgumentOutOfRangeException("length", "The specified offset and length exceed the buffer length"); |
|
72 LibUsbRequest req = new LibUsbRequest(); |
|
73 req.Descriptor.Index = index; |
|
74 req.Descriptor.LangID = langId; |
|
75 req.Descriptor.Recipient = (byte)UsbEndpointDirection.EndpointIn & 0x1F; |
|
76 req.Descriptor.Type = descriptorType; |
|
77 req.Timeout = UsbConstants.DEFAULT_TIMEOUT; |
|
78 int ret; |
|
79 fixed (Byte* b = buffer) { |
|
80 DeviceIoControl(DeviceHandle, LibUsbIoCtl.GET_DESCRIPTOR, ref req, LibUsbRequest.Size, (IntPtr)(b + offset), length, out ret); |
|
81 } |
|
82 return ret; |
|
83 } |
|
84 public override int BulkRead(byte endpoint, byte[] buffer, int offset, int length) { |
|
85 return PipeTransfer(endpoint, false, false, buffer, offset, length, 0); |
|
86 } |
|
87 public override int BulkWrite(byte endpoint, byte[] buffer, int offset, int length) { |
|
88 return PipeTransfer(endpoint, true, false, buffer, offset, length, 0); |
|
89 } |
|
90 public override int InterruptRead(byte endpoint, byte[] buffer, int offset, int length) { |
|
91 return PipeTransfer(endpoint, false, false, buffer, offset, length, 0); |
|
92 } |
|
93 public override int InterruptWrite(byte endpoint, byte[] buffer, int offset, int length) { |
|
94 return PipeTransfer(endpoint, true, false, buffer, offset, length, 0); |
|
95 } |
|
96 public unsafe override int ControlRead(UsbControlRequestType requestType, byte request, short value, short index, byte[] buffer, int offset, int length) { |
|
97 if (buffer == null) buffer = new Byte[0]; |
|
98 if (offset < 0 || length < 0 || offset + length > buffer.Length) throw new ArgumentOutOfRangeException("length", "The specified offset and length exceed the buffer length"); |
|
99 int code; |
|
100 LibUsbRequest req = new LibUsbRequest(); |
|
101 PrepareControlTransfer(requestType, request, value, index, length, ref req, out code); |
|
102 int ret; |
|
103 fixed (Byte* b = buffer) { |
|
104 DeviceIoControl(DeviceHandle, code, ref req, LibUsbRequest.Size, (IntPtr)(b + offset), length, out ret); |
|
105 } |
|
106 return ret; |
|
107 } |
|
108 public unsafe override int ControlWrite(UsbControlRequestType requestType, byte request, short value, short index, byte[] buffer, int offset, int length) { |
|
109 Byte[] inbuffer = new Byte[length + LibUsbRequest.Size]; |
|
110 if (length > 0) Buffer.BlockCopy(buffer, offset, inbuffer, LibUsbRequest.Size, length); |
|
111 int code; |
|
112 fixed (Byte* inbufferp = inbuffer) |
|
113 PrepareControlTransfer(requestType, request, value, index, length, ref *((LibUsbRequest*)inbufferp), out code); |
|
114 int ret; |
|
115 DeviceIoControl(DeviceHandle, code, inbuffer, length + LibUsbRequest.Size, null, 0, out ret); |
|
116 return length; |
|
117 //ret -= LibUsbRequest.Size; |
|
118 //if (ret <= 0) return 0; |
|
119 //return ret; |
|
120 } |
|
121 void PrepareControlTransfer(UsbControlRequestType requestType, byte request, short value, short index, int length, ref LibUsbRequest req, out int code) { |
|
122 code = LibUsbIoCtl.CONTROL_TRANSFER; |
|
123 req.Timeout = UsbConstants.DEFAULT_TIMEOUT; |
|
124 req.Control.RequestType = (Byte)requestType; |
|
125 req.Control.Request = request; |
|
126 req.Control.Value = (ushort)value; |
|
127 req.Control.Index = (ushort)index; |
|
128 req.Control.Length = (ushort)length; |
|
129 switch ((UsbControlRequestType)((int)requestType & (0x03 << 5))) { |
|
130 case UsbControlRequestType.TypeStandard: |
|
131 switch ((UsbStandardRequest)request) { |
|
132 case UsbStandardRequest.GetStatus: |
|
133 req.Status.Recipient = (int)requestType & 0x1F; |
|
134 req.Status.Index = index; |
|
135 code = LibUsbIoCtl.GET_STATUS; |
|
136 break; |
|
137 case UsbStandardRequest.ClearFeature: |
|
138 req.Feature.Recipient = (int)requestType & 0x1F; |
|
139 req.Feature.ID = value; |
|
140 req.Feature.Index = index; |
|
141 code = LibUsbIoCtl.CLEAR_FEATURE; |
|
142 break; |
|
143 case UsbStandardRequest.SetFeature: |
|
144 req.Feature.Recipient = (int)requestType & 0x1F; |
|
145 req.Feature.ID = value; |
|
146 req.Feature.Index = index; |
|
147 code = LibUsbIoCtl.SET_FEATURE; |
|
148 break; |
|
149 case UsbStandardRequest.GetDescriptor: |
|
150 req.Descriptor.Recipient = (int)requestType & 0x1F; |
|
151 req.Descriptor.Type = (value >> 8) & 0xFF; |
|
152 req.Descriptor.Index = value & 0xFF; |
|
153 req.Descriptor.LangID = index; |
|
154 code = LibUsbIoCtl.GET_DESCRIPTOR; |
|
155 break; |
|
156 case UsbStandardRequest.SetDescriptor: |
|
157 req.Descriptor.Recipient = (int)requestType & 0x1F; |
|
158 req.Descriptor.Type = (value >> 8) & 0xFF; |
|
159 req.Descriptor.Index = value & 0xFF; |
|
160 req.Descriptor.LangID = index; |
|
161 code = LibUsbIoCtl.SET_DESCRIPTOR; |
|
162 break; |
|
163 case UsbStandardRequest.GetConfiguration: |
|
164 code = LibUsbIoCtl.GET_CONFIGURATION; |
|
165 break; |
|
166 case UsbStandardRequest.SetConfiguration: |
|
167 req.Config.ID = value; |
|
168 code = LibUsbIoCtl.SET_CONFIGURATION; |
|
169 break; |
|
170 case UsbStandardRequest.GetInterface: |
|
171 req.Iface.ID = index; |
|
172 code = LibUsbIoCtl.GET_INTERFACE; |
|
173 break; |
|
174 case UsbStandardRequest.SetInterface: |
|
175 req.Iface.ID = index; |
|
176 req.Iface.AlternateID = value; |
|
177 code = LibUsbIoCtl.SET_INTERFACE; |
|
178 break; |
|
179 default: |
|
180 throw new ArgumentException(String.Format("Invalid request: 0x{0:X8}", request)); |
|
181 } |
|
182 break; |
|
183 case UsbControlRequestType.TypeVendor: |
|
184 case UsbControlRequestType.TypeClass: |
|
185 req.Vendor.Type = ((byte)requestType >> 5) & 0x03; |
|
186 req.Vendor.Recipient = (int)requestType & 0x1F; |
|
187 req.Vendor.Request = (int)request; |
|
188 req.Vendor.ID = value; |
|
189 req.Vendor.Index = index; |
|
190 code = ((byte)requestType & 0x80) != 0 ? LibUsbIoCtl.VENDOR_READ : LibUsbIoCtl.VENDOR_WRITE; |
|
191 break; |
|
192 case UsbControlRequestType.TypeReserved: |
|
193 default: |
|
194 throw new ArgumentException(String.Format("Invalid or unsupported request type: 0x{0:X8}", requestType)); |
|
195 } |
|
196 } |
|
197 |
|
198 unsafe int PipeTransfer(Byte epnum, Boolean write, Boolean isochronous, Byte[] buffer, int offset, int length, int packetsize) { |
|
199 if (offset < 0 || length < 0 || offset + length > buffer.Length) throw new ArgumentOutOfRangeException("length", "The specified offset and length exceed the buffer length"); |
|
200 LibUsbRequest req = new LibUsbRequest(); |
|
201 req.Endpoint.ID = epnum; |
|
202 req.Endpoint.PacketSize = packetsize; |
|
203 req.Timeout = UsbConstants.DEFAULT_TIMEOUT; |
|
204 fixed (Byte* b = buffer) { |
|
205 if (write) { |
|
206 int cltCode = isochronous ? LibUsbIoCtl.ISOCHRONOUS_WRITE : LibUsbIoCtl.INTERRUPT_OR_BULK_WRITE; |
|
207 int transfered = 0; |
|
208 while (length > 0) { |
|
209 int ret; |
|
210 DeviceIoControl(DeviceHandle, cltCode, ref req, LibUsbRequest.Size, (IntPtr)(b + offset), Math.Min(Int16.MaxValue, length), out ret); |
|
211 if (ret <= 0) throw new System.IO.EndOfStreamException(); |
|
212 length -= ret; |
|
213 offset += ret; |
|
214 transfered += ret; |
|
215 } |
|
216 return transfered; |
|
217 } else { |
|
218 int cltCode = isochronous ? LibUsbIoCtl.ISOCHRONOUS_READ : LibUsbIoCtl.INTERRUPT_OR_BULK_READ; |
|
219 int ret; |
|
220 DeviceIoControl(DeviceHandle, cltCode, ref req, LibUsbRequest.Size, (IntPtr)(b + offset), Math.Min(UInt16.MaxValue, length), out ret); |
|
221 return ret; |
|
222 } |
|
223 } |
|
224 } |
|
225 public void PipeReset(byte pipeID) { |
|
226 LibUsbRequest req = new LibUsbRequest(); |
|
227 req.Endpoint.ID = pipeID; |
|
228 req.Timeout = UsbConstants.DEFAULT_TIMEOUT; |
|
229 int ret; |
|
230 DeviceIoControl(DeviceHandle, LibUsbIoCtl.RESET_ENDPOINT, ref req, LibUsbRequest.Size, null, 0, out ret); |
|
231 } |
|
232 |
|
233 private unsafe void DeviceIoControl(SafeHandle hDevice, int IoControlCode, [In] ref LibUsbRequest InBuffer, int nInBufferSize, Byte[] OutBuffer, int nOutBufferSize, out int pBytesReturned) { |
|
234 fixed (LibUsbRequest* InBufferPtr = &InBuffer) { |
|
235 fixed (Byte* OutBufferPtr = OutBuffer) { |
|
236 DeviceIoControl(hDevice, IoControlCode, (IntPtr)InBufferPtr, nInBufferSize, (IntPtr)OutBufferPtr, nOutBufferSize, out pBytesReturned); |
|
237 } |
|
238 } |
|
239 } |
|
240 private unsafe void DeviceIoControl(SafeHandle hDevice, int IoControlCode, Byte[] InBuffer, int nInBufferSize, Byte[] OutBuffer, int nOutBufferSize, out int pBytesReturned) { |
|
241 fixed (Byte* InBufferPtr = InBuffer, OutBufferPtr = OutBuffer) { |
|
242 DeviceIoControl(hDevice, IoControlCode, (IntPtr)InBufferPtr, nInBufferSize, (IntPtr)OutBufferPtr, nOutBufferSize, out pBytesReturned); |
|
243 } |
|
244 } |
|
245 private unsafe void DeviceIoControl(SafeHandle hDevice, int IoControlCode, [In] ref LibUsbRequest InBuffer, int nInBufferSize, IntPtr OutBuffer, int nOutBufferSize, out int pBytesReturned) { |
|
246 fixed (LibUsbRequest* InBufferPtr = &InBuffer) { |
|
247 DeviceIoControl(hDevice, IoControlCode, (IntPtr)InBufferPtr, nInBufferSize, OutBuffer, nOutBufferSize, out pBytesReturned); |
|
248 } |
|
249 } |
|
250 private unsafe void DeviceIoControl(SafeHandle hDevice, int IoControlCode, IntPtr InBuffer, int nInBufferSize, IntPtr OutBuffer, int nOutBufferSize, out int pBytesReturned) { |
|
251 using (ManualResetEvent evt = new ManualResetEvent(false)) { |
|
252 NativeOverlapped overlapped = new NativeOverlapped(); |
|
253 overlapped.EventHandle = evt.SafeWaitHandle.DangerousGetHandle(); |
|
254 if (Kernel32.DeviceIoControl(hDevice, IoControlCode, InBuffer, nInBufferSize, OutBuffer, nOutBufferSize, out pBytesReturned, &overlapped)) |
|
255 return; |
|
256 int err = Marshal.GetLastWin32Error(); |
|
257 if (err != 997) throw new Win32Exception(err); |
|
258 evt.WaitOne(); |
|
259 if (!Kernel32.GetOverlappedResult(hDevice, &overlapped, out pBytesReturned, false)) |
|
260 throw new Win32Exception(Marshal.GetLastWin32Error()); |
|
261 } |
|
262 } |
|
263 } |
|
264 } |