504 lines
16 KiB
Plaintext
504 lines
16 KiB
Plaintext
|
|
using System;
|
||
|
|
using System.Threading;
|
||
|
|
using System.Runtime.InteropServices;
|
||
|
|
using DTS.Common.DASResource;
|
||
|
|
using DTS.Common.USBFramework;
|
||
|
|
using DTS.Common.Utilities.Logging;
|
||
|
|
using DTS.Common.Interface.Connection;
|
||
|
|
using DTS.Common.Classes.Connection;
|
||
|
|
using System.Threading.Tasks;
|
||
|
|
|
||
|
|
namespace DTS.Common.WINUSBConnection
|
||
|
|
{
|
||
|
|
public class WINUSBConnection : IConnection
|
||
|
|
{
|
||
|
|
/// <summary>
|
||
|
|
/// returns true if the unit is currently soft disconnected
|
||
|
|
/// </summary>
|
||
|
|
public bool IsSoftDisconnected { get; private set; } = false;
|
||
|
|
/// <summary>
|
||
|
|
/// connect the unit
|
||
|
|
/// :note does nothing for WINUSB as we have no disconnect option yet:
|
||
|
|
/// </summary>
|
||
|
|
public void SoftConnect()
|
||
|
|
{
|
||
|
|
}
|
||
|
|
/// <summary>
|
||
|
|
/// disconnect the unit
|
||
|
|
/// :note does nothing for WINUSB as we have no disconnect option yet:
|
||
|
|
/// </summary>
|
||
|
|
public void SoftDisconnect()
|
||
|
|
{
|
||
|
|
|
||
|
|
}
|
||
|
|
void IConnection.KeepAliveErrorReceived()
|
||
|
|
{
|
||
|
|
|
||
|
|
}
|
||
|
|
public string GetConnectionData() { return ""; }
|
||
|
|
private readonly DeviceManagement _wusbDeviceManagement;
|
||
|
|
private WinUsbDevice _sliceDev;
|
||
|
|
|
||
|
|
public event EventHandler OnDisconnected;
|
||
|
|
public double GetCurrentUploadRate() { return 0D; }
|
||
|
|
public double GetCurrentDownloadRate() { return 0D; }
|
||
|
|
|
||
|
|
public bool Connected { get; private set; }
|
||
|
|
|
||
|
|
public string ConnectString { get; private set; }
|
||
|
|
|
||
|
|
public System.Net.Sockets.SocketFlags Flags { get; set; }
|
||
|
|
|
||
|
|
private class AUSBRecFix
|
||
|
|
{
|
||
|
|
public readonly AsyncCallback Callback;
|
||
|
|
public WinUSBRecAsyncResult USBResult;
|
||
|
|
|
||
|
|
public AUSBRecFix(AsyncCallback callback, WinUSBRecAsyncResult usbResult)
|
||
|
|
{
|
||
|
|
Callback = callback;
|
||
|
|
USBResult = usbResult;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public class WinUSBRecAsyncResult : IAsyncResult
|
||
|
|
{
|
||
|
|
public object AsyncState { get; set; }
|
||
|
|
public WaitHandle AsyncWaitHandle { get; set; }
|
||
|
|
public bool CompletedSynchronously { get; set; }
|
||
|
|
public bool IsCompleted { get; set; }
|
||
|
|
public byte[] Buffer { get; set; }
|
||
|
|
public int Offset { get; set; }
|
||
|
|
public int Size { get; set; }
|
||
|
|
|
||
|
|
public WinUSBRecAsyncResult(object state)
|
||
|
|
{
|
||
|
|
AsyncState = state;
|
||
|
|
AsyncWaitHandle = new ManualResetEvent(false);
|
||
|
|
CompletedSynchronously = false;
|
||
|
|
IsCompleted = false;
|
||
|
|
Buffer = null;
|
||
|
|
Offset = 0;
|
||
|
|
Size = 0;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public WINUSBConnection()
|
||
|
|
{
|
||
|
|
Disposed = false;
|
||
|
|
ConnectString = string.Empty;
|
||
|
|
Connected = false;
|
||
|
|
_wusbDeviceManagement = new DeviceManagement();
|
||
|
|
_sliceDev = null;
|
||
|
|
}
|
||
|
|
|
||
|
|
#region Exit handling
|
||
|
|
|
||
|
|
~WINUSBConnection()
|
||
|
|
{
|
||
|
|
Dispose(false);
|
||
|
|
try
|
||
|
|
{
|
||
|
|
APILogger.Log("failed to dispose WINUSBConnection - finalizer is disposing now.");
|
||
|
|
}
|
||
|
|
catch { }
|
||
|
|
}
|
||
|
|
|
||
|
|
public void Dispose()
|
||
|
|
{
|
||
|
|
Dispose(true);
|
||
|
|
|
||
|
|
// Use SupressFinalize in case a subclass
|
||
|
|
// of this type implements a finalizer.
|
||
|
|
GC.SuppressFinalize(this);
|
||
|
|
}
|
||
|
|
|
||
|
|
protected volatile bool Disposed;
|
||
|
|
protected int Disposing;
|
||
|
|
private readonly Mutex _usbConnectionMutex = new Mutex(false, "USBConnection");
|
||
|
|
|
||
|
|
protected virtual void Dispose(bool disposing)
|
||
|
|
{
|
||
|
|
if (Interlocked.Exchange(ref Disposing, 1) != 0) { return; }
|
||
|
|
|
||
|
|
// make sure we're not already disposed
|
||
|
|
if (Disposed)
|
||
|
|
return;
|
||
|
|
|
||
|
|
if (disposing)
|
||
|
|
{
|
||
|
|
try
|
||
|
|
{
|
||
|
|
if (_wusbDeviceManagement != null)
|
||
|
|
{
|
||
|
|
_wusbDeviceManagement.Dispose();
|
||
|
|
}
|
||
|
|
DisposeSliceDev();
|
||
|
|
}
|
||
|
|
catch
|
||
|
|
{
|
||
|
|
// we have to suppress all exceptions here
|
||
|
|
}
|
||
|
|
}
|
||
|
|
Connected = false;
|
||
|
|
Disposed = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
#endregion
|
||
|
|
public void Create(string connectString)
|
||
|
|
{
|
||
|
|
//Create(connectString); this is recursive!
|
||
|
|
}
|
||
|
|
public void Create(string connectString, string hostIPAddress)
|
||
|
|
{
|
||
|
|
ConnectString = connectString;
|
||
|
|
}
|
||
|
|
|
||
|
|
#region Disconnect
|
||
|
|
|
||
|
|
public IAsyncResult BeginDisconnect(bool reuseSocket,
|
||
|
|
AsyncCallback cb,
|
||
|
|
Object state)
|
||
|
|
{
|
||
|
|
if (_sliceDev == null)
|
||
|
|
{
|
||
|
|
throw new NotConnectedException("WINUSBConnection is null");
|
||
|
|
}
|
||
|
|
|
||
|
|
if (cb == null)
|
||
|
|
{
|
||
|
|
throw new ArgumentException("WINUSBConnection.BeginDisconnect: callback is null");
|
||
|
|
}
|
||
|
|
|
||
|
|
Connected = false;
|
||
|
|
var rar = new WinUSBRecAsyncResult(state);
|
||
|
|
if (!ThreadPool.QueueUserWorkItem(NetCallbackFix, new AUSBRecFix(cb, rar)))
|
||
|
|
{
|
||
|
|
throw new Exception(DASResource.Strings.WINUSBConnection_BeginDisconnect_Err1);
|
||
|
|
}
|
||
|
|
return rar;
|
||
|
|
}
|
||
|
|
|
||
|
|
public void EndDisconnect(IAsyncResult asyncResult)
|
||
|
|
{
|
||
|
|
if (_sliceDev == null)
|
||
|
|
{
|
||
|
|
throw new NotConnectedException("WINUSBConnection is null");
|
||
|
|
}
|
||
|
|
|
||
|
|
var rar = asyncResult as WinUSBRecAsyncResult;
|
||
|
|
if (rar == null)
|
||
|
|
{
|
||
|
|
throw new ArgumentException("WINUSBConnection.EndDisconnect: argument is not a WinUSBRecAsyncResult");
|
||
|
|
}
|
||
|
|
|
||
|
|
DisposeSliceDev();
|
||
|
|
|
||
|
|
((ManualResetEvent)rar.AsyncWaitHandle).Set();
|
||
|
|
}
|
||
|
|
private void DisposeSliceDev()
|
||
|
|
{
|
||
|
|
_usbConnectionMutex.WaitOne(1000);
|
||
|
|
try
|
||
|
|
{
|
||
|
|
if (null != _sliceDev)
|
||
|
|
{
|
||
|
|
_sliceDev.Dispose();
|
||
|
|
_sliceDev = null;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
catch (Exception ex) { APILogger.Log("could not release USB handle ", ex); }
|
||
|
|
finally { _usbConnectionMutex.ReleaseMutex(); }
|
||
|
|
}
|
||
|
|
#endregion
|
||
|
|
|
||
|
|
#region Accept
|
||
|
|
|
||
|
|
public IAsyncResult BeginAccept(AsyncCallback callback,
|
||
|
|
Object state)
|
||
|
|
{
|
||
|
|
throw new NotSupportedException();
|
||
|
|
}
|
||
|
|
|
||
|
|
public IConnection EndAccept(IAsyncResult asyncResult)
|
||
|
|
{
|
||
|
|
throw new NotSupportedException();
|
||
|
|
}
|
||
|
|
|
||
|
|
#endregion
|
||
|
|
|
||
|
|
#region Listen
|
||
|
|
|
||
|
|
public void Listen(int backlog)
|
||
|
|
{
|
||
|
|
throw new NotSupportedException();
|
||
|
|
}
|
||
|
|
|
||
|
|
#endregion
|
||
|
|
|
||
|
|
#region Bind
|
||
|
|
|
||
|
|
public void Bind(int port)
|
||
|
|
{
|
||
|
|
throw new NotSupportedException();
|
||
|
|
}
|
||
|
|
|
||
|
|
#endregion
|
||
|
|
|
||
|
|
#region Connect
|
||
|
|
|
||
|
|
public IAsyncResult BeginConnect(AsyncCallback cb, object state)
|
||
|
|
{
|
||
|
|
if (_sliceDev != null || Connected)
|
||
|
|
{
|
||
|
|
throw new NotConnectedException("WINUSBConnection.BeginConnect: already connected");
|
||
|
|
}
|
||
|
|
|
||
|
|
if (cb == null)
|
||
|
|
{
|
||
|
|
throw new ArgumentException("WINUSBConnection.BeginConnect: callback is null");
|
||
|
|
}
|
||
|
|
|
||
|
|
var rar = new WinUSBRecAsyncResult(state);
|
||
|
|
var ausbrf = new AUSBRecFix(cb, rar);
|
||
|
|
(ausbrf?.USBResult.AsyncWaitHandle as ManualResetEvent).Reset();
|
||
|
|
if (!ThreadPool.QueueUserWorkItem(NetCallbackFix, ausbrf))
|
||
|
|
{
|
||
|
|
throw new Exception(DASResource.Strings.WINUSBConnection_BeginConnect_Err1);
|
||
|
|
}
|
||
|
|
rar.AsyncWaitHandle.WaitOne();
|
||
|
|
return rar;
|
||
|
|
}
|
||
|
|
|
||
|
|
private void NetCallbackFix(object obj)
|
||
|
|
{
|
||
|
|
var ausbrf = obj as AUSBRecFix;
|
||
|
|
try
|
||
|
|
{
|
||
|
|
ausbrf?.Callback(ausbrf.USBResult);
|
||
|
|
}
|
||
|
|
catch (Exception ex)
|
||
|
|
{
|
||
|
|
APILogger.Log("MessageBox", DASResource.Strings.Communication_WinUSBConnectionCallbackFailed, ex);
|
||
|
|
}
|
||
|
|
finally
|
||
|
|
{
|
||
|
|
(ausbrf?.USBResult.AsyncWaitHandle as ManualResetEvent).Set();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public void EndConnect(IAsyncResult ar)
|
||
|
|
{
|
||
|
|
_usbConnectionMutex.WaitOne();
|
||
|
|
var rar = ar as WinUSBRecAsyncResult;
|
||
|
|
try
|
||
|
|
{
|
||
|
|
if (_sliceDev != null || Connected)
|
||
|
|
{
|
||
|
|
throw new NotConnectedException("WINUSBConnection.EndConnect: already connected");
|
||
|
|
}
|
||
|
|
|
||
|
|
if (rar == null)
|
||
|
|
{
|
||
|
|
throw new ArgumentException("WINUSBConnection.EndConnect: argument is not a WinUSBRecAsyncResult");
|
||
|
|
}
|
||
|
|
|
||
|
|
if (string.IsNullOrEmpty(ConnectString))
|
||
|
|
{
|
||
|
|
throw new ArgumentException("WINUSBConnection.EndConnect: ConnectString is invalid");
|
||
|
|
}
|
||
|
|
|
||
|
|
_sliceDev = new WinUsbDevice();
|
||
|
|
}
|
||
|
|
catch (Exception ex) { APILogger.Log("exception in connecting USB", ex); throw; }
|
||
|
|
finally { _usbConnectionMutex.ReleaseMutex(); }
|
||
|
|
var success = _sliceDev.GetDeviceHandle(ConnectString);
|
||
|
|
if (!success)
|
||
|
|
{
|
||
|
|
Connected = false;
|
||
|
|
|
||
|
|
DisposeSliceDev();
|
||
|
|
((ManualResetEvent)rar.AsyncWaitHandle).Set();
|
||
|
|
throw new NotConnectedException("WINUSBConnection.EndConnect: connect failed");
|
||
|
|
}
|
||
|
|
if (_sliceDev.InitializeDevice())
|
||
|
|
{
|
||
|
|
Connected = true;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
var error = DeviceManagementDeclarations.GetLastError();
|
||
|
|
try
|
||
|
|
{
|
||
|
|
var w32 = new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
|
||
|
|
APILogger.Log("WINUSBConnection.EndConnect: connect failed: ", w32.Message);
|
||
|
|
}
|
||
|
|
catch { }
|
||
|
|
throw new NotConnectedException("WINUSBConnection.EndConnect: connect failed (" + error + ")");
|
||
|
|
}
|
||
|
|
((ManualResetEvent)rar.AsyncWaitHandle).Set();
|
||
|
|
}
|
||
|
|
|
||
|
|
#endregion
|
||
|
|
|
||
|
|
#region Send
|
||
|
|
|
||
|
|
public IAsyncResult BeginSend(byte[] buffer, int offset, int size,
|
||
|
|
AsyncCallback cb, object state)
|
||
|
|
{
|
||
|
|
if (!Connected || _sliceDev == null)
|
||
|
|
{
|
||
|
|
throw new NotConnectedException("WINUSBConnection is not connected");
|
||
|
|
}
|
||
|
|
|
||
|
|
if (buffer == null || buffer.Length == 0 || offset < 0 || size < 0 || offset + size > buffer.Length || cb == null)
|
||
|
|
{
|
||
|
|
throw new ArgumentException("WINUSBConnection.EndConnect: invalid parameters");
|
||
|
|
}
|
||
|
|
|
||
|
|
var rar = new WinUSBRecAsyncResult(state)
|
||
|
|
{
|
||
|
|
Buffer = buffer,
|
||
|
|
Offset = offset,
|
||
|
|
Size = size
|
||
|
|
};
|
||
|
|
if (!ThreadPool.QueueUserWorkItem(NetCallbackFix, new AUSBRecFix(cb, rar)))
|
||
|
|
{
|
||
|
|
throw new Exception(DASResource.Strings.WINUSBConnection_BeginSend_Err1);
|
||
|
|
}
|
||
|
|
return rar;
|
||
|
|
}
|
||
|
|
|
||
|
|
public int EndSend(IAsyncResult ar)
|
||
|
|
{
|
||
|
|
if (!Connected || _sliceDev == null)
|
||
|
|
{
|
||
|
|
throw new NotConnectedException("WINUSBConnection is not connected");
|
||
|
|
}
|
||
|
|
|
||
|
|
var rar = ar as WinUSBRecAsyncResult;
|
||
|
|
if (rar == null)
|
||
|
|
{
|
||
|
|
throw new ArgumentException("WINUSBConnection.EndSend: argument is not a WinUSBRecAsyncResult");
|
||
|
|
}
|
||
|
|
|
||
|
|
var bytes = new byte[rar.Size];
|
||
|
|
Buffer.BlockCopy(rar.Buffer, 0, bytes, 0, rar.Size);
|
||
|
|
if (_sliceDev.MyDevInfo.UseHybridBulkIntMode)
|
||
|
|
{
|
||
|
|
if (!_sliceDev.SendViaInterruptTransfer(ref bytes, (uint)rar.Size))
|
||
|
|
{
|
||
|
|
Connected = false;
|
||
|
|
((ManualResetEvent)rar.AsyncWaitHandle).Set();
|
||
|
|
throw new Exception(DASResource.Strings.WINUSBConnection_EndSend_Err1);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
if (!_sliceDev.SendViaBulkTransfer(ref bytes, (uint)rar.Size))
|
||
|
|
{
|
||
|
|
Connected = false;
|
||
|
|
((ManualResetEvent)rar.AsyncWaitHandle).Set();
|
||
|
|
throw new Exception(DASResource.Strings.WINUSBConnection_EndSend_Err1);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
((ManualResetEvent)rar.AsyncWaitHandle).Set();
|
||
|
|
return rar.Size;
|
||
|
|
}
|
||
|
|
|
||
|
|
public Task<int> SendAsync(byte[] sendBuffer, int bufferStartOffset, int bufferSizeToSend)
|
||
|
|
{
|
||
|
|
return Task<int>.Factory.FromAsync(
|
||
|
|
(callback, state) => BeginSend(sendBuffer, bufferStartOffset, bufferSizeToSend, callback, state),
|
||
|
|
EndSend, state: null
|
||
|
|
);
|
||
|
|
}
|
||
|
|
#endregion
|
||
|
|
|
||
|
|
#region Receive
|
||
|
|
|
||
|
|
public IAsyncResult BeginReceive(byte[] buffer, int offset, int size,
|
||
|
|
AsyncCallback cb, object state)
|
||
|
|
{
|
||
|
|
if (!Connected || _sliceDev == null)
|
||
|
|
{
|
||
|
|
throw new NotConnectedException("WINUSBConnection is not connected");
|
||
|
|
}
|
||
|
|
|
||
|
|
if (buffer == null || buffer.Length == 0 || offset < 0 || size < 0 || offset + size > buffer.Length || cb == null)
|
||
|
|
{
|
||
|
|
throw new ArgumentException("WINUSBConnection.EndConnect: invalid parameters");
|
||
|
|
}
|
||
|
|
|
||
|
|
var rar = new WinUSBRecAsyncResult(state)
|
||
|
|
{
|
||
|
|
Buffer = buffer,
|
||
|
|
Offset = offset,
|
||
|
|
Size = size
|
||
|
|
};
|
||
|
|
if (!ThreadPool.QueueUserWorkItem(NetCallbackFix, new AUSBRecFix(cb, rar)))
|
||
|
|
{
|
||
|
|
throw new Exception(DASResource.Strings.WINUSBConnection_BeginReceive_Err1);
|
||
|
|
}
|
||
|
|
return rar;
|
||
|
|
}
|
||
|
|
|
||
|
|
public int EndReceive(IAsyncResult ar)
|
||
|
|
{
|
||
|
|
if (!Connected || _sliceDev == null)
|
||
|
|
{
|
||
|
|
throw new NotConnectedException("WINUSBConnection is not connected");
|
||
|
|
}
|
||
|
|
|
||
|
|
var rar = ar as WinUSBRecAsyncResult;
|
||
|
|
if (rar == null)
|
||
|
|
{
|
||
|
|
throw new ArgumentException("WINUSBConnection.EndReceive: argument is not a WinUSBRecAsyncResult");
|
||
|
|
}
|
||
|
|
|
||
|
|
try
|
||
|
|
{
|
||
|
|
uint bytesRead = 0;
|
||
|
|
var bytes = new byte[rar.Size];
|
||
|
|
var success = true;
|
||
|
|
while (success && 0 == bytesRead)
|
||
|
|
{
|
||
|
|
_sliceDev.ReadViaBulkTransfer(Convert.ToByte(_sliceDev.MyDevInfo.BulkInPipe),
|
||
|
|
(uint)rar.Size,
|
||
|
|
ref bytes,
|
||
|
|
ref bytesRead,
|
||
|
|
ref success);
|
||
|
|
if (0 == bytesRead)
|
||
|
|
{
|
||
|
|
Thread.Sleep(1);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (success)
|
||
|
|
{
|
||
|
|
Buffer.BlockCopy(bytes, 0, rar.Buffer, 0, (int)bytesRead);
|
||
|
|
rar.Size = (int)bytesRead;
|
||
|
|
((ManualResetEvent)rar.AsyncWaitHandle).Set();
|
||
|
|
return rar.Size;
|
||
|
|
}
|
||
|
|
// the connection is now closed
|
||
|
|
Connected = false;
|
||
|
|
((ManualResetEvent)rar.AsyncWaitHandle).Set();
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
catch (Exception ex)
|
||
|
|
{
|
||
|
|
Connected = false;
|
||
|
|
if (!(ex is NullReferenceException))
|
||
|
|
{
|
||
|
|
APILogger.LogString("WINUSBConnection.EndReceive: Exception caught");
|
||
|
|
APILogger.LogException(ex);
|
||
|
|
}
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
#endregion
|
||
|
|
}
|
||
|
|
}
|