| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381 |
- #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.Runtime.InteropServices;
- #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
- {
- private int _opened, _closed;
- private 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)
- {
- 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)
- {
- 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)
- {
- 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)
- {
- 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)
- {
- return Read(buffer, 0, buffer.Length);
- }
- /// <inheritdoc />
- public override int EndRead(IAsyncResult 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)
- {
- 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)
- {
- 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)
- {
- Write(buffer, 0, buffer.Length);
- }
- /// <inheritdoc />
- public override void EndWrite(IAsyncResult 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 override sealed 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 override sealed int WriteTimeout
- {
- get;
- set;
- }
- }
- }
|