| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334 |
- #region License
- /* Copyright 2012-2013 James F. Bellinger <http://www.zer7.com/software/hidsharp>
- Permission to use, copy, modify, and/or distribute this software for any
- purpose with or without fee is hereby granted, provided that the above
- copyright notice and this permission notice appear in all copies.
- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
- #endregion
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Runtime.InteropServices;
- using System.Threading;
- #pragma warning disable 420
- namespace HidSharp {
- /// <summary>
- /// Communicates with a USB HID class device.
- /// </summary>
- [ComVisible(true), Guid("0C263D05-0D58-4c6c-AEA7-EB9E0C5338A2")]
- public abstract class HidStream : Stream {
- int _opened, _closed;
- volatile int _refCount;
- internal class CommonOutputReport {
- public byte[] Bytes;
- public bool DoneOK, Feature;
- public volatile bool Done;
- }
- internal HidStream() {
- ReadTimeout = 3000;
- WriteTimeout = 3000;
- }
- internal static int GetTimeout(int startTime, int timeout) {
- return Math.Min(timeout, Math.Max(0, startTime + timeout - Environment.TickCount));
- }
- internal int CommonRead(byte[] buffer, int offset, int count, Queue<byte[]> queue) {
- Throw.If.OutOfRange(buffer, offset, count);
- if (count == 0) {
- return 0;
- }
- int readTimeout = ReadTimeout;
- int startTime = Environment.TickCount;
- int timeout;
- HandleAcquireIfOpenOrFail();
- try {
- lock (queue) {
- while (true) {
- if (queue.Count > 0) {
- byte[] packet = queue.Dequeue();
- count = Math.Min(count, packet.Length);
- Array.Copy(packet, 0, buffer, offset, count);
- return count;
- }
-
- timeout = GetTimeout(startTime, readTimeout);
- if (!Monitor.Wait(queue, timeout)) {
- throw new TimeoutException();
- }
- }
- }
- } finally {
- HandleRelease();
- }
- }
- internal void CommonWrite(byte[] buffer, int offset, int count,
- Queue<CommonOutputReport> queue,
- bool feature, int maxOutputReportLength) {
- Throw.If.OutOfRange(buffer, offset, count);
- count = Math.Min(count, maxOutputReportLength);
- if (count == 0) {
- return;
- }
- int writeTimeout = WriteTimeout;
- int startTime = Environment.TickCount;
- int timeout;
-
- HandleAcquireIfOpenOrFail();
- try {
- lock (queue) {
- while (true) {
- if (queue.Count == 0) {
- byte[] packet = new byte[count];
- Array.Copy(buffer, offset, packet, 0, count);
- var outputReport = new CommonOutputReport { Bytes = packet, Feature = feature };
- queue.Enqueue(outputReport);
- Monitor.PulseAll(queue);
-
- while (true) {
- if (outputReport.Done) {
- if (!outputReport.DoneOK) {
- throw new IOException();
- }
- return;
- }
-
- timeout = GetTimeout(startTime, writeTimeout);
- if (!Monitor.Wait(queue, timeout)) {
- throw new TimeoutException();
- }
- }
- }
-
- timeout = GetTimeout(startTime, writeTimeout);
- if (!Monitor.Wait(queue, timeout)) {
- throw new TimeoutException();
- }
- }
- }
- } finally {
- HandleRelease();
- }
- }
- /// <exclude />
- public override void Flush() {
-
- }
- /// <summary>
- /// Sends a Get Feature setup request.
- /// </summary>
- /// <param name="buffer">The buffer to fill. Place the Report ID in the first byte.</param>
- public void GetFeature(byte[] buffer) {
- Throw.If.Null(buffer, "buffer");
- GetFeature(buffer, 0, buffer.Length);
- }
- /// <summary>
- /// Sends a Get Feature setup request.
- /// </summary>
- /// <param name="buffer">The buffer to fill. Place the Report ID in the byte at index <paramref name="offset"/>.</param>
- /// <param name="offset">The index in the buffer to begin filling with data.</param>
- /// <param name="count">The number of bytes in the feature request.</param>
- public abstract void GetFeature(byte[] buffer, int offset, int count);
- public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state) {
- Throw.If.OutOfRange(buffer, offset, count);
- return AsyncResult<int>.BeginOperation(() => Read(buffer, offset, count), callback, state);
- }
- /// <summary>
- /// Reads HID Input Reports.
- /// </summary>
- /// <returns>The data read.</returns>
- public byte[] Read() {
- byte[] buffer = new byte[Device.MaxInputReportLength];
- int bytes = Read(buffer);
- Array.Resize(ref buffer, bytes);
- return buffer;
- }
- /// <summary>
- /// Reads HID Input Reports.
- /// </summary>
- /// <param name="buffer">The buffer to place the reports into.</param>
- /// <returns>The number of bytes read.</returns>
- public int Read(byte[] buffer) {
- Throw.If.Null(buffer, "buffer");
- return Read(buffer, 0, buffer.Length);
- }
- /// <inheritdoc />
- public override int EndRead(IAsyncResult asyncResult) {
- Throw.If.Null(asyncResult, "asyncResult");
- return ((AsyncResult<int>)asyncResult).EndOperation();
- }
- /// <exclude />
- public override long Seek(long offset, SeekOrigin origin) {
- throw new NotSupportedException();
- }
- /// <summary>
- /// Sends a Set Feature setup request.
- /// </summary>
- /// <param name="buffer">The buffer of data to send. Place the Report ID in the first byte.</param>
- public void SetFeature(byte[] buffer) {
- Throw.If.Null(buffer, "buffer");
- SetFeature(buffer, 0, buffer.Length);
- }
- /// <summary>
- /// Sends a Set Feature setup request.
- /// </summary>
- /// <param name="buffer">The buffer of data to send. Place the Report ID in the byte at index <paramref name="offset"/>.</param>
- /// <param name="offset">The index in the buffer to start the write from.</param>
- /// <param name="count">The number of bytes in the feature request.</param>
- public abstract void SetFeature(byte[] buffer, int offset, int count);
- /// <exclude />
- public override void SetLength(long value) {
- throw new NotSupportedException();
- }
- public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) {
- Throw.If.OutOfRange(buffer, offset, count);
- return AsyncResult<int>.BeginOperation(delegate {
- Write(buffer, offset, count);
- return 0;
- }, callback, state);
- }
- internal void HandleInitAndOpen() {
- _opened = 1;
- _refCount = 1;
- }
- internal bool HandleClose() {
- return 0 == Interlocked.CompareExchange(ref _closed, 1, 0) && _opened != 0;
- }
- internal bool HandleAcquire() {
- while (true) {
- int refCount = _refCount;
- if (refCount == 0) {
- return false;
- }
-
- if (refCount == Interlocked.CompareExchange
- (ref _refCount, refCount + 1, refCount)) {
- return true;
- }
- }
- }
- internal void HandleAcquireIfOpenOrFail() {
- if (_closed != 0 || !HandleAcquire()) {
- throw new IOException("Closed.");
- }
- }
- internal void HandleRelease() {
- if (0 == Interlocked.Decrement(ref _refCount)) {
- if (_opened != 0) {
- HandleFree();
- }
- }
- }
- internal abstract void HandleFree();
- /// <summary>
- /// Writes an HID Output Report to the device.
- /// </summary>
- /// <param name="buffer">The buffer containing the report. Place the Report ID in the first byte.</param>
- public void Write(byte[] buffer) {
- Throw.If.Null(buffer, "buffer");
- Write(buffer, 0, buffer.Length);
- }
- /// <inheritdoc />
- public override void EndWrite(IAsyncResult asyncResult) {
- Throw.If.Null(asyncResult, "asyncResult");
- ((AsyncResult<int>)asyncResult).EndOperation();
- }
- /// <exclude />
- public override bool CanRead {
- get { return true; }
- }
- /// <exclude />
- public override bool CanSeek {
- get { return false; }
- }
- /// <exclude />
- public override bool CanWrite {
- get { return true; }
- }
- /// <exclude />
- public override bool CanTimeout {
- get { return true; }
- }
- /// <summary>
- /// Gets the <see cref="HidDevice"/> associated with this stream.
- /// </summary>
- public abstract HidDevice Device {
- get;
- }
- /// <exclude />
- public override long Length {
- get { throw new NotSupportedException(); }
- }
- /// <exclude />
- public override long Position {
- get { throw new NotSupportedException(); }
- set { throw new NotSupportedException(); }
- }
- /// <summary>
- /// The maximum amount of time, in milliseconds, to wait for to receive a HID report.
- ///
- /// The default is 3000 milliseconds.
- /// To disable the timeout, set this to <see cref="Timeout.Infinite"/>.
- /// </summary>
- public sealed override int ReadTimeout {
- get;
- set;
- }
- /// <summary>
- /// The maximum amount of time, in milliseconds, to wait for the device to acknowledge a HID report.
- ///
- /// The default is 3000 milliseconds.
- /// To disable the timeout, set this to <see cref="Timeout.Infinite"/>.
- /// </summary>
- public sealed override int WriteTimeout {
- get;
- set;
- }
- }
- }
|