HidStream.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. #region License
  2. /* Copyright 2012-2013 James F. Bellinger <http://www.zer7.com/software/hidsharp>
  3. Permission to use, copy, modify, and/or distribute this software for any
  4. purpose with or without fee is hereby granted, provided that the above
  5. copyright notice and this permission notice appear in all copies.
  6. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  7. WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  8. MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  9. ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  10. WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  11. ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  12. OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
  13. #endregion
  14. using System.Runtime.InteropServices;
  15. #pragma warning disable 420
  16. namespace HidSharp
  17. {
  18. /// <summary>
  19. /// Communicates with a USB HID class device.
  20. /// </summary>
  21. [ComVisible(true), Guid("0C263D05-0D58-4c6c-AEA7-EB9E0C5338A2")]
  22. public abstract class HidStream : Stream
  23. {
  24. private int _opened, _closed;
  25. private volatile int _refCount;
  26. internal class CommonOutputReport
  27. {
  28. public byte[] Bytes;
  29. public bool DoneOK, Feature;
  30. public volatile bool Done;
  31. }
  32. internal HidStream()
  33. {
  34. ReadTimeout = 3000;
  35. WriteTimeout = 3000;
  36. }
  37. internal static int GetTimeout(int startTime, int timeout)
  38. {
  39. return Math.Min(timeout, Math.Max(0, startTime + timeout - Environment.TickCount));
  40. }
  41. internal int CommonRead(byte[] buffer, int offset, int count, Queue<byte[]> queue)
  42. {
  43. if (count == 0)
  44. {
  45. return 0;
  46. }
  47. int readTimeout = ReadTimeout;
  48. int startTime = Environment.TickCount;
  49. int timeout;
  50. HandleAcquireIfOpenOrFail();
  51. try
  52. {
  53. lock (queue)
  54. {
  55. while (true)
  56. {
  57. if (queue.Count > 0)
  58. {
  59. byte[] packet = queue.Dequeue();
  60. count = Math.Min(count, packet.Length);
  61. Array.Copy(packet, 0, buffer, offset, count);
  62. return count;
  63. }
  64. timeout = GetTimeout(startTime, readTimeout);
  65. if (!Monitor.Wait(queue, timeout))
  66. {
  67. throw new TimeoutException();
  68. }
  69. }
  70. }
  71. }
  72. finally
  73. {
  74. HandleRelease();
  75. }
  76. }
  77. internal void CommonWrite(byte[] buffer, int offset, int count,
  78. Queue<CommonOutputReport> queue,
  79. bool feature, int maxOutputReportLength)
  80. {
  81. count = Math.Min(count, maxOutputReportLength);
  82. if (count == 0)
  83. {
  84. return;
  85. }
  86. int writeTimeout = WriteTimeout;
  87. int startTime = Environment.TickCount;
  88. int timeout;
  89. HandleAcquireIfOpenOrFail();
  90. try
  91. {
  92. lock (queue)
  93. {
  94. while (true)
  95. {
  96. if (queue.Count == 0)
  97. {
  98. byte[] packet = new byte[count];
  99. Array.Copy(buffer, offset, packet, 0, count);
  100. var outputReport = new CommonOutputReport { Bytes = packet, Feature = feature };
  101. queue.Enqueue(outputReport);
  102. Monitor.PulseAll(queue);
  103. while (true)
  104. {
  105. if (outputReport.Done)
  106. {
  107. if (!outputReport.DoneOK)
  108. {
  109. throw new IOException();
  110. }
  111. return;
  112. }
  113. timeout = GetTimeout(startTime, writeTimeout);
  114. if (!Monitor.Wait(queue, timeout))
  115. {
  116. throw new TimeoutException();
  117. }
  118. }
  119. }
  120. timeout = GetTimeout(startTime, writeTimeout);
  121. if (!Monitor.Wait(queue, timeout))
  122. {
  123. throw new TimeoutException();
  124. }
  125. }
  126. }
  127. }
  128. finally
  129. {
  130. HandleRelease();
  131. }
  132. }
  133. /// <exclude />
  134. public override void Flush()
  135. {
  136. }
  137. /// <summary>
  138. /// Sends a Get Feature setup request.
  139. /// </summary>
  140. /// <param name="buffer">The buffer to fill. Place the Report ID in the first byte.</param>
  141. public void GetFeature(byte[] buffer)
  142. {
  143. GetFeature(buffer, 0, buffer.Length);
  144. }
  145. /// <summary>
  146. /// Sends a Get Feature setup request.
  147. /// </summary>
  148. /// <param name="buffer">The buffer to fill. Place the Report ID in the byte at index <paramref name="offset"/>.</param>
  149. /// <param name="offset">The index in the buffer to begin filling with data.</param>
  150. /// <param name="count">The number of bytes in the feature request.</param>
  151. public abstract void GetFeature(byte[] buffer, int offset, int count);
  152. public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
  153. {
  154. return AsyncResult<int>.BeginOperation(() => Read(buffer, offset, count), callback, state);
  155. }
  156. /// <summary>
  157. /// Reads HID Input Reports.
  158. /// </summary>
  159. /// <returns>The data read.</returns>
  160. public byte[] Read()
  161. {
  162. byte[] buffer = new byte[Device.MaxInputReportLength];
  163. int bytes = Read(buffer);
  164. Array.Resize(ref buffer, bytes);
  165. return buffer;
  166. }
  167. /// <summary>
  168. /// Reads HID Input Reports.
  169. /// </summary>
  170. /// <param name="buffer">The buffer to place the reports into.</param>
  171. /// <returns>The number of bytes read.</returns>
  172. public int Read(byte[] buffer)
  173. {
  174. return Read(buffer, 0, buffer.Length);
  175. }
  176. /// <inheritdoc />
  177. public override int EndRead(IAsyncResult asyncResult)
  178. {
  179. return ((AsyncResult<int>)asyncResult).EndOperation();
  180. }
  181. /// <exclude />
  182. public override long Seek(long offset, SeekOrigin origin)
  183. {
  184. throw new NotSupportedException();
  185. }
  186. /// <summary>
  187. /// Sends a Set Feature setup request.
  188. /// </summary>
  189. /// <param name="buffer">The buffer of data to send. Place the Report ID in the first byte.</param>
  190. public void SetFeature(byte[] buffer)
  191. {
  192. SetFeature(buffer, 0, buffer.Length);
  193. }
  194. /// <summary>
  195. /// Sends a Set Feature setup request.
  196. /// </summary>
  197. /// <param name="buffer">The buffer of data to send. Place the Report ID in the byte at index <paramref name="offset"/>.</param>
  198. /// <param name="offset">The index in the buffer to start the write from.</param>
  199. /// <param name="count">The number of bytes in the feature request.</param>
  200. public abstract void SetFeature(byte[] buffer, int offset, int count);
  201. /// <exclude />
  202. public override void SetLength(long value)
  203. {
  204. throw new NotSupportedException();
  205. }
  206. public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
  207. {
  208. return AsyncResult<int>.BeginOperation(delegate
  209. {
  210. Write(buffer, offset, count);
  211. return 0;
  212. }, callback, state);
  213. }
  214. internal void HandleInitAndOpen()
  215. {
  216. _opened = 1;
  217. _refCount = 1;
  218. }
  219. internal bool HandleClose()
  220. {
  221. return 0 == Interlocked.CompareExchange(ref _closed, 1, 0) && _opened != 0;
  222. }
  223. internal bool HandleAcquire()
  224. {
  225. while (true)
  226. {
  227. int refCount = _refCount;
  228. if (refCount == 0)
  229. {
  230. return false;
  231. }
  232. if (refCount == Interlocked.CompareExchange
  233. (ref _refCount, refCount + 1, refCount))
  234. {
  235. return true;
  236. }
  237. }
  238. }
  239. internal void HandleAcquireIfOpenOrFail()
  240. {
  241. if (_closed != 0 || !HandleAcquire())
  242. {
  243. throw new IOException("Closed.");
  244. }
  245. }
  246. internal void HandleRelease()
  247. {
  248. if (0 == Interlocked.Decrement(ref _refCount))
  249. {
  250. if (_opened != 0)
  251. {
  252. HandleFree();
  253. }
  254. }
  255. }
  256. internal abstract void HandleFree();
  257. /// <summary>
  258. /// Writes an HID Output Report to the device.
  259. /// </summary>
  260. /// <param name="buffer">The buffer containing the report. Place the Report ID in the first byte.</param>
  261. public void Write(byte[] buffer)
  262. {
  263. Write(buffer, 0, buffer.Length);
  264. }
  265. /// <inheritdoc />
  266. public override void EndWrite(IAsyncResult asyncResult)
  267. {
  268. ((AsyncResult<int>)asyncResult).EndOperation();
  269. }
  270. /// <exclude />
  271. public override bool CanRead
  272. {
  273. get { return true; }
  274. }
  275. /// <exclude />
  276. public override bool CanSeek
  277. {
  278. get { return false; }
  279. }
  280. /// <exclude />
  281. public override bool CanWrite
  282. {
  283. get { return true; }
  284. }
  285. /// <exclude />
  286. public override bool CanTimeout
  287. {
  288. get { return true; }
  289. }
  290. /// <summary>
  291. /// Gets the <see cref="HidDevice"/> associated with this stream.
  292. /// </summary>
  293. public abstract HidDevice Device
  294. {
  295. get;
  296. }
  297. /// <exclude />
  298. public override long Length
  299. {
  300. get { throw new NotSupportedException(); }
  301. }
  302. /// <exclude />
  303. public override long Position
  304. {
  305. get { throw new NotSupportedException(); }
  306. set { throw new NotSupportedException(); }
  307. }
  308. /// <summary>
  309. /// The maximum amount of time, in milliseconds, to wait for to receive a HID report.
  310. ///
  311. /// The default is 3000 milliseconds.
  312. /// To disable the timeout, set this to <see cref="Timeout.Infinite"/>.
  313. /// </summary>
  314. public override sealed int ReadTimeout
  315. {
  316. get;
  317. set;
  318. }
  319. /// <summary>
  320. /// The maximum amount of time, in milliseconds, to wait for the device to acknowledge a HID report.
  321. ///
  322. /// The default is 3000 milliseconds.
  323. /// To disable the timeout, set this to <see cref="Timeout.Infinite"/>.
  324. /// </summary>
  325. public override sealed int WriteTimeout
  326. {
  327. get;
  328. set;
  329. }
  330. }
  331. }