// Copyright © 2006-2010 Travis Robinson. All rights reserved. // // website: http://sourceforge.net/projects/libusbdotnet // e-mail: libusbdotnet@gmail.com // // This program is free software; you can redistribute it and/or modify it // under the terms of the GNU General Public License as published by the // Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License // for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. or // visit www.gnu.org. // // using System; using System.Runtime.InteropServices; using System.Threading; using System.Windows.Forms; using LibUsbDotNet.Internal; using LibUsbDotNet.Main; namespace LibUsbDotNet { /// /// Contains methods for retrieving data from a or endpoint using the overloaded functions or a event. /// /// /// /// Before using the event, the property must be set to true. /// While the property is True, the overloaded functions cannot be used. /// /// public class UsbEndpointReader : UsbEndpointBase { private static int mDefReadBufferSize = 4096; private bool mDataReceivedEnabled; private int mReadBufferSize; private Thread mReadThread; private ThreadPriority mReadThreadPriority = ThreadPriority.Normal; internal UsbEndpointReader(UsbDevice usbDevice, int readBufferSize, ReadEndpointID readEndpointID, EndpointType endpointType) : base(usbDevice, (Byte) readEndpointID, endpointType) { mReadBufferSize = readBufferSize; } /// /// Default read buffer size when using the event. /// /// /// This value can be bypassed using the second parameter of the method. /// The default is 4096. /// public static int DefReadBufferSize { get { return mDefReadBufferSize; } set { mDefReadBufferSize = value; } } /// /// Gets/Sets a value indicating if the event should be used. /// /// /// If DataReceivedEnabled is true the functions cannot be used. /// public virtual bool DataReceivedEnabled { get { return mDataReceivedEnabled; } set { if (value != mDataReceivedEnabled) { StartStopReadThread(); } } } /// /// Size of the read buffer in bytes for the event. /// /// /// Setting a large values, for example 64K will yield a lower number of and a higher data rate. /// public int ReadBufferSize { get { return mReadBufferSize; } set { mReadBufferSize = value; } } /// /// Gets/Sets the Priority level for the read thread when is true. /// public ThreadPriority ReadThreadPriority { get { return mReadThreadPriority; } set { mReadThreadPriority = value; } } /// /// Reads data from the current . /// /// The buffer to store the recieved data in. /// Maximum time to wait for the transfer to complete. If the transfer times out, the IO operation will be cancelled. /// Number of bytes actually transferred. /// /// . on success. /// public virtual ErrorCode Read(byte[] buffer, int timeout, out int transferLength) { return Read(buffer, 0, buffer.Length, timeout, out transferLength); } /// /// Reads data from the current . /// /// The buffer to store the recieved data in. /// The position in buffer to start storing the data. /// The maximum number of bytes to receive. /// Maximum time to wait for the transfer to complete. If the transfer times out, the IO operation will be cancelled. /// Number of bytes actually transferred. /// /// . on success. /// public virtual ErrorCode Read(IntPtr buffer, int offset, int count, int timeout, out int transferLength) { return Transfer(buffer, offset, count, timeout, out transferLength); } /// /// Reads data from the current . /// /// The buffer to store the recieved data in. /// The position in buffer to start storing the data. /// The maximum number of bytes to receive. /// Maximum time to wait for the transfer to complete. If the transfer times out, the IO operation will be cancelled. /// Number of bytes actually transferred. /// /// . on success. /// public virtual ErrorCode Read(byte[] buffer, int offset, int count, int timeout, out int transferLength) { return Transfer(buffer, offset, count, timeout, out transferLength); } /// /// Reads data from the current . /// /// The buffer to store the recieved data in. /// The position in buffer to start storing the data. /// The maximum number of bytes to receive. /// Maximum time to wait for the transfer to complete. If the transfer times out, the IO operation will be cancelled. /// Number of bytes actually transferred. /// /// . on success. /// public virtual ErrorCode Read(object buffer, int offset, int count, int timeout, out int transferLength) { return Transfer(buffer, offset, count, timeout, out transferLength); } /// /// Reads data from the current . /// /// The buffer to store the recieved data in. /// Maximum time to wait for the transfer to complete. If the transfer times out, the IO operation will be cancelled. /// Number of bytes actually transferred. /// /// . on success. /// public virtual ErrorCode Read(object buffer, int timeout, out int transferLength) { return Transfer(buffer, 0, Marshal.SizeOf(buffer), timeout, out transferLength); } /// /// Reads/discards data from the enpoint until no more data is available. /// /// Alwats returns public virtual ErrorCode ReadFlush() { byte[] bufDummy = new byte[64]; int iTransferred; int iBufCount = 0; while (Read(bufDummy, 10, out iTransferred) == ErrorCode.None && iBufCount < 128) { iBufCount++; } return ErrorCode.None; } private static void ReadData(object context) { UsbTransfer overlappedTransferContext = (UsbTransfer) context; UsbEndpointReader reader = (UsbEndpointReader) overlappedTransferContext.EndpointBase; reader.mDataReceivedEnabled = true; EventHandler dataReceivedEnabledChangedEvent; dataReceivedEnabledChangedEvent = reader.DataReceivedEnabledChanged; if (!ReferenceEquals(dataReceivedEnabledChangedEvent,null)) dataReceivedEnabledChangedEvent(reader, new DataReceivedEnabledChangedEventArgs(reader.mDataReceivedEnabled)); overlappedTransferContext.Reset(); byte[] buf = new byte[reader.mReadBufferSize]; try { while (!overlappedTransferContext.IsCancelled) { int iTransferLength; ErrorCode eReturn = reader.Transfer(buf, 0, buf.Length, Timeout.Infinite, out iTransferLength); if (eReturn == ErrorCode.None) { EventHandler temp = reader.DataReceived; if (!ReferenceEquals(temp, null) && !overlappedTransferContext.IsCancelled) { temp(reader, new EndpointDataEventArgs(buf, iTransferLength)); } continue; } if (eReturn != ErrorCode.IoTimedOut) break; } } catch (ThreadAbortException) { UsbError.Error(ErrorCode.ReceiveThreadTerminated,0, "ReadData:Read thread aborted.", reader); } finally { reader.Abort(); reader.mDataReceivedEnabled = false; dataReceivedEnabledChangedEvent = reader.DataReceivedEnabledChanged; if (!ReferenceEquals(dataReceivedEnabledChangedEvent, null)) dataReceivedEnabledChangedEvent(reader, new DataReceivedEnabledChangedEventArgs(reader.mDataReceivedEnabled)); } } private void StartReadThread() { mReadThread = new Thread(ReadData); mReadThread.Priority = ReadThreadPriority; mReadThread.Start(TransferContext); Thread.Sleep(1); Application.DoEvents(); } private bool StopReadThread() { Abort(); Thread.Sleep(1); Application.DoEvents(); DateTime dtStart = DateTime.Now; while (mReadThread.IsAlive && ((DateTime.Now - dtStart).TotalSeconds < 5)) // 5 sec fail-safe { Thread.Sleep(100); Application.DoEvents(); } if (mReadThread.IsAlive) { UsbError.Error(ErrorCode.ReceiveThreadTerminated,0, "Failed stopping read thread.", this); mReadThread.Abort(); return false; } return true; } private void StartStopReadThread() { if (IsDisposed) throw new ObjectDisposedException(GetType().FullName); if (mDataReceivedEnabled) { StopReadThread(); } else { StartReadThread(); } } /// /// The DataReceived Event is fired when new data arrives for the current . /// /// To use the DataReceived event, must be set to truw. public virtual event EventHandler DataReceived; /// /// The Event is fired when the event is started or stopped. /// public virtual event EventHandler DataReceivedEnabledChanged; internal override UsbTransfer CreateTransferContext() { return new OverlappedTransferContext(this); } } }