PageRenderTime 45ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 1ms

/NUSB/Controller/Windows32USBController.cs

https://github.com/thenathanjones/nusb
C# | 476 lines | 404 code | 55 blank | 17 comment | 36 complexity | 871c969c080143c9e3bd38c606713ca6 MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Runtime.InteropServices;
  5. using System.Text;
  6. using System.Threading;
  7. using Microsoft.Win32.SafeHandles;
  8. using NUSB.Interop;
  9. namespace NUSB.Controller
  10. {
  11. /// <summary>
  12. /// USB Controller that uses the Windows API to interact with USB devices
  13. /// </summary>
  14. public sealed class Windows32USBController : IUSBController
  15. {
  16. private string _devicePath;
  17. private bool _separateHandles;
  18. private SafeFileHandle _writeHandle;
  19. private SafeFileHandle _readHandle;
  20. private SafeFileHandle _handle;
  21. /// <summary>
  22. /// Lock to control access to the handles. To prevent closing a handle at the same time as cancelling a pending IO
  23. /// </summary>
  24. private readonly object _cancelIOLock = new object();
  25. public void Dispose()
  26. {
  27. Disconnect();
  28. }
  29. public void Initialise(string pathToDevice, bool separateHandles)
  30. {
  31. _devicePath = pathToDevice;
  32. _separateHandles = separateHandles;
  33. Connect();
  34. }
  35. public void Connect()
  36. {
  37. OpenHandle();
  38. }
  39. private void OpenHandle()
  40. {
  41. if (_separateHandles)
  42. {
  43. // open separate handles for reading and writing
  44. OpenSeparateHandles();
  45. }
  46. else
  47. {
  48. // open a handle which will handle both reading and writing
  49. OpenSingleHandle();
  50. }
  51. }
  52. private void CleanupHandles()
  53. {
  54. lock (_cancelIOLock)
  55. {
  56. CleanupHandle(_handle);
  57. CleanupHandle(_readHandle);
  58. CleanupHandle(_writeHandle);
  59. _handle = _readHandle = _writeHandle = null;
  60. }
  61. }
  62. private void CleanupHandle(SafeFileHandle handle)
  63. {
  64. if (handle != null && !handle.IsClosed)
  65. {
  66. InteropKernel32.CloseHandle(handle);
  67. handle.Close();
  68. }
  69. }
  70. private void OpenSingleHandle()
  71. {
  72. _handle = InteropKernel32.CreateFile(_devicePath,
  73. (uint)InteropKernel32.AccessRights.GENERIC_READ |
  74. (uint)InteropKernel32.AccessRights.GENERIC_WRITE,
  75. (uint)InteropKernel32.ShareModes.FILE_SHARE_WRITE |
  76. (uint)InteropKernel32.ShareModes.FILE_SHARE_READ,
  77. IntPtr.Zero,
  78. (uint)InteropKernel32.CreationDispositions.OPEN_EXISTING,
  79. (uint)InteropKernel32.FileAttributeFlags.FILE_FLAG_OVERLAPPED |
  80. (uint)InteropKernel32.FileAttributeFlags.FILE_FLAG_NO_BUFFERING,
  81. IntPtr.Zero);
  82. if (_handle == null || _handle.IsInvalid)
  83. {
  84. CleanupHandles();
  85. throw new Exception("Oh noes Cap'n, something is wrong with the handles!");
  86. }
  87. // use this handle for both reads and writes
  88. _readHandle = _handle;
  89. _writeHandle = _handle;
  90. }
  91. private void OpenSeparateHandles()
  92. {
  93. // open a handle for writing
  94. _writeHandle = InteropKernel32.CreateFile(_devicePath,
  95. (uint)InteropKernel32.AccessRights.GENERIC_WRITE,
  96. (uint)InteropKernel32.ShareModes.FILE_SHARE_WRITE |
  97. (uint)InteropKernel32.ShareModes.FILE_SHARE_READ,
  98. IntPtr.Zero,
  99. (uint)InteropKernel32.CreationDispositions.OPEN_EXISTING,
  100. (uint)InteropKernel32.FileAttributeFlags.FILE_FLAG_OVERLAPPED,
  101. IntPtr.Zero);
  102. // open a handle for reading
  103. _readHandle = InteropKernel32.CreateFile(_devicePath,
  104. (uint)InteropKernel32.AccessRights.GENERIC_READ,
  105. (uint)InteropKernel32.ShareModes.FILE_SHARE_WRITE |
  106. (uint)InteropKernel32.ShareModes.FILE_SHARE_READ,
  107. IntPtr.Zero,
  108. (uint)InteropKernel32.CreationDispositions.OPEN_EXISTING,
  109. (uint)InteropKernel32.FileAttributeFlags.FILE_FLAG_OVERLAPPED,
  110. IntPtr.Zero);
  111. if (_writeHandle == null || _readHandle == null || _writeHandle.IsInvalid || _readHandle.IsInvalid)
  112. {
  113. CleanupHandles();
  114. throw new Exception("Oh noes Cap'n, something is wrong with the handles!");
  115. }
  116. }
  117. public void Disconnect()
  118. {
  119. CleanupHandles();
  120. }
  121. public unsafe void WriteControl(uint controlCode, byte[] writeBuffer)
  122. {
  123. uint bytesReturned = 0;
  124. fixed (byte* inBuffer = writeBuffer)
  125. {
  126. var success = false;
  127. try
  128. {
  129. success = InteropKernel32.DeviceIoControl(_writeHandle,
  130. controlCode,
  131. inBuffer,
  132. writeBuffer == null ? 0 : (uint)writeBuffer.Length,
  133. null,
  134. 0,
  135. ref bytesReturned,
  136. null);
  137. }
  138. catch (ObjectDisposedException)
  139. {
  140. throw new Exception("File handle already closed");
  141. }
  142. // retrieve the error on failures
  143. if (!success) { HandleIOError(false); }
  144. }
  145. }
  146. private void HandleIOError(bool ignoreOverlapped)
  147. {
  148. var lastWin32Error = Marshal.GetLastWin32Error();
  149. // if the error is something other than pending IO (i.e. because we're using overlapped)
  150. if (!ignoreOverlapped || lastWin32Error != (int)InteropCommon.Win32Errors.ERROR_IO_PENDING)
  151. {
  152. throw new Exception("Unknown Win32 Error occurred: " + lastWin32Error);
  153. }
  154. }
  155. public unsafe void WriteControlOverlapped(uint controlCode, byte[] writeBuffer)
  156. {
  157. var completedEvent = new ManualResetEvent(false);
  158. uint bytesReturned = 0;
  159. var outOverlapped = new Overlapped();
  160. outOverlapped.EventHandleIntPtr = completedEvent.SafeWaitHandle.DangerousGetHandle();
  161. NativeOverlapped* outNativeOverlapped = outOverlapped.Pack(null, null);
  162. try
  163. {
  164. fixed (byte* inBuffer = writeBuffer)
  165. {
  166. var success = false;
  167. try
  168. {
  169. success = InteropKernel32.DeviceIoControl(_writeHandle,
  170. controlCode,
  171. inBuffer,
  172. writeBuffer == null ? 0 : (uint)writeBuffer.Length,
  173. null,
  174. 0,
  175. ref bytesReturned,
  176. outNativeOverlapped);
  177. }
  178. catch (ObjectDisposedException)
  179. {
  180. throw new Exception("File handle already closed");
  181. }
  182. if (!success)
  183. {
  184. HandleIOError(true);
  185. CancelOverlapped(_writeHandle, completedEvent);
  186. }
  187. }
  188. }
  189. finally
  190. {
  191. Overlapped.Free(outNativeOverlapped);
  192. }
  193. }
  194. private void CancelOverlapped(SafeFileHandle handle, ManualResetEvent completedEvent)
  195. {
  196. lock (_cancelIOLock)
  197. {
  198. if (!completedEvent.WaitOne(OVERLAPPED_TIMEOUT) && handle != null && !handle.IsClosed && !handle.IsInvalid)
  199. InteropKernel32.CancelIo(handle);
  200. }
  201. }
  202. public void WriteClear(uint controlCode)
  203. {
  204. // cancel pending operations
  205. lock (_cancelIOLock)
  206. {
  207. InteropKernel32.CancelIo(_readHandle);
  208. InteropKernel32.CancelIo(_writeHandle);
  209. }
  210. WriteControl(controlCode, null);
  211. }
  212. public unsafe void ReadControl(uint controlCode, byte[] writeBuffer, byte[] readBuffer)
  213. {
  214. uint bytesReturned = 0;
  215. fixed (byte* inBuffer = writeBuffer)
  216. {
  217. fixed (byte* outBuffer = readBuffer)
  218. {
  219. var success = false;
  220. try
  221. {
  222. success = InteropKernel32.DeviceIoControl(_readHandle,
  223. controlCode,
  224. inBuffer,
  225. writeBuffer == null ? 0 : (uint)writeBuffer.Length,
  226. outBuffer,
  227. readBuffer == null ? 0 : (uint)readBuffer.Length,
  228. ref bytesReturned,
  229. null);
  230. }
  231. catch (ObjectDisposedException)
  232. {
  233. throw new Exception("File handle already closed");
  234. }
  235. if (!success) { HandleIOError(false); }
  236. }
  237. }
  238. }
  239. public unsafe void ReadControlOverlapped(uint controlCode, byte[] writeBuffer, byte[] readBuffer)
  240. {
  241. var completedEvent = new ManualResetEvent(false);
  242. uint bytesReturned = 0;
  243. var inOverlapped = new Overlapped();
  244. inOverlapped.EventHandleIntPtr = completedEvent.SafeWaitHandle.DangerousGetHandle();
  245. NativeOverlapped* inNativeOverlapped = inOverlapped.Pack(null, null);
  246. try
  247. {
  248. fixed (byte* inBuffer = writeBuffer)
  249. {
  250. fixed (byte* outBuffer = readBuffer)
  251. {
  252. var success = false;
  253. try
  254. {
  255. success = InteropKernel32.DeviceIoControl(_readHandle,
  256. controlCode,
  257. inBuffer,
  258. writeBuffer == null ? 0 : (uint) writeBuffer.Length,
  259. outBuffer,
  260. readBuffer == null ? 0 : (uint) readBuffer.Length,
  261. ref bytesReturned,
  262. inNativeOverlapped);
  263. }
  264. catch (ObjectDisposedException)
  265. {
  266. throw new Exception("File handle already closed");
  267. }
  268. if (!success)
  269. {
  270. HandleIOError(true);
  271. CancelOverlapped(_readHandle, completedEvent);
  272. }
  273. }
  274. }
  275. }
  276. finally
  277. {
  278. Overlapped.Free(inNativeOverlapped);
  279. }
  280. }
  281. public unsafe void Write(byte[] writeBuffer)
  282. {
  283. uint bytesWritten = 0;
  284. fixed (byte* inBuffer = writeBuffer)
  285. {
  286. var success = false;
  287. try
  288. {
  289. success = InteropKernel32.WriteFile(_writeHandle,
  290. inBuffer,
  291. (uint)writeBuffer.Length,
  292. ref bytesWritten,
  293. null);
  294. }
  295. catch (ObjectDisposedException)
  296. {
  297. throw new Exception("File handle already closed");
  298. }
  299. if (!success) { HandleIOError(false); }
  300. }
  301. }
  302. public unsafe void WriteOverlapped(byte[] writeBuffer)
  303. {
  304. var completedEvent = new ManualResetEvent(false);
  305. uint bytesWritten = 0;
  306. var outOverlapped = new Overlapped();
  307. outOverlapped.EventHandleIntPtr = completedEvent.SafeWaitHandle.DangerousGetHandle();
  308. NativeOverlapped* outNativeOverlapped = outOverlapped.Pack(null, null);
  309. try
  310. {
  311. // send the data to the device
  312. fixed (byte* inBuffer = writeBuffer)
  313. {
  314. var success = false;
  315. try
  316. {
  317. success = InteropKernel32.WriteFile(_writeHandle,
  318. inBuffer,
  319. (uint)writeBuffer.Length,
  320. ref bytesWritten,
  321. outNativeOverlapped);
  322. }
  323. catch (ObjectDisposedException)
  324. {
  325. throw new Exception("File handle already closed");
  326. }
  327. if (!success)
  328. {
  329. HandleIOError(true);
  330. CancelOverlapped(_writeHandle, completedEvent);
  331. }
  332. }
  333. }
  334. finally
  335. {
  336. Overlapped.Free(outNativeOverlapped);
  337. }
  338. }
  339. public unsafe void Read(byte[] readBuffer)
  340. {
  341. uint bytesRead = 0;
  342. fixed (byte* outBuffer = readBuffer)
  343. {
  344. var success = false;
  345. try
  346. {
  347. success = InteropKernel32.ReadFile(_readHandle,
  348. outBuffer,
  349. (uint)readBuffer.Length,
  350. ref bytesRead,
  351. null);
  352. }
  353. catch (ObjectDisposedException)
  354. {
  355. throw new Exception("File handle already closed");
  356. }
  357. if (!success)
  358. {
  359. HandleIOError(false);
  360. }
  361. }
  362. }
  363. public unsafe void ReadOverlapped(byte[] readBuffer)
  364. {
  365. var completedEvent = new ManualResetEvent(false);
  366. uint bytesRead = 0;
  367. var inOverlapped = new Overlapped();
  368. inOverlapped.EventHandleIntPtr = completedEvent.SafeWaitHandle.DangerousGetHandle();
  369. NativeOverlapped* inNativeOverlapped = inOverlapped.Pack(null, null);
  370. try
  371. {
  372. // send the data to the device
  373. fixed (byte* outBuffer = readBuffer)
  374. {
  375. var success = false;
  376. try
  377. {
  378. success = InteropKernel32.ReadFile(_readHandle,
  379. outBuffer,
  380. (uint)readBuffer.Length,
  381. ref bytesRead,
  382. inNativeOverlapped);
  383. }
  384. catch (ObjectDisposedException)
  385. {
  386. throw new Exception("File handle already closed");
  387. }
  388. if (!success)
  389. {
  390. HandleIOError(true);
  391. CancelOverlapped(_readHandle, completedEvent);
  392. }
  393. }
  394. }
  395. finally
  396. {
  397. // clean up
  398. Overlapped.Free(inNativeOverlapped);
  399. }
  400. }
  401. public unsafe void HidSetFeature(byte[] reportBuffer)
  402. {
  403. fixed (byte* inBuffer = reportBuffer)
  404. {
  405. var success = false;
  406. try
  407. {
  408. success = InteropHID.HidD_SetFeature(_writeHandle,
  409. inBuffer,
  410. (uint)reportBuffer.Length);
  411. }
  412. catch (ObjectDisposedException)
  413. {
  414. throw new Exception("File handle already closed");
  415. }
  416. if (!success) { HandleIOError(false); }
  417. }
  418. }
  419. private const int OVERLAPPED_TIMEOUT = 2000;
  420. }
  421. }