/*****************************************************************************
 * Copyright (C) Cinterion Wireless Modules GmbH
 *****************************************************************************
 * Project: Quinn-CE-USB-Driver
 *****************************************************************************
 * Author : Siemens AG Berlin COM WM RD SW
 *****************************************************************************
 * $Workfile: Y:/z_ms-d/src/working/Quinn/HcUsbDrv/VirtualPort.cpp $
 * $Revision: /main/ms-d/76 $
 * $Date: 2014-04-09 15:59:00 $
 * $Author: pmackent $
 *****************************************************************************
 * Target:
 *  Microsoft Windows CE 5.0
 *****************************************************************************
 * Description:
 *  Quinn-USB-Driver: Virtual COM port port class CVirtualPort
 *  implementation
 *****************************************************************************
 *
 * $Log: Y:/z_ms-d/src/working/Quinn/HcUsbDrv/VirtualPort.cpp$ 
 * 
 * /main/ms-d/76   2014-04-09 15:59:00   pmackent 
 * Fix for endless loop when module shuts down during WWAN connection 
 * 
 * /main/ms-d/75   2014-04-07 11:27:18   mwohler 
 * Abort write() on cmd port if port has been closed 
 * 
 * /main/ms-d/74   2014-04-03 13:45:55   mwohler 
 * Enumerate multiple rmnet interfaces properly.
 * Added support for PLSx Rel. 2.
 * 
 * /main/ms-d/73   2013-06-19 17:36:58   thinze 
 * improved debug messages
 * added handling of device request NTF_NETWORK_CONNECTION (ignore device request)
 * 
 * /main/ms-d/72   2012-12-05 13:22:29   thinze 
 * bugfix: added missing closeHandle 
 * 
 * /main/ms-d/71   2012-02-07 14:57:17   ctna0152 
 * bugfix: possible lock on detach if open and detach happen at same time
 *
 * /main/ms-d/70   2011-06-16 12:00:23   ctna0152
 * bugfix for IPIS100073142 (endless loop in case of error)
 *
 * /main/ms-d/69   2011-06-06 12:59:51   ctna0152
 * bugfix IPIS100070728 - release pending wait events on detach
 *
 * /main/ms-d/68   2011-03-03 09:56:59   ctna0450
 * Performance improvements
 *
 * /main/ms-d/67   2011-03-02 10:50:40   ctna0450
 * Avoid sending of unnecessary zero length packages
 *
 * /main/ms-d/66   2011-02-14 14:07:34   ctna0152
 * fix for IPIS100069778
 * removed USE_DEVICE_REQUEST_REDIRECT encapsulation
 *
 * /main/ms-d/65   2011-02-02 10:28:00   ctna0450
 * Removed unused USB-Reset workaround for Uranus USB problems
 *
 * /main/ms-d/64   2010-12-13 14:23:44   ctna0152
 * added USB reset handling
 *
 * /main/ms-d/63   2010-08-17 14:41:55   ctna0450
 * Use dynamic array for port list to be able to handle more than 4 virtual ports
 *
 * /main/ms-d/62   2010-08-04 13:54:46   ctna0450
 * Unified enumeration process
 *
 * /main/ms-d/61   2009-09-07 13:50:13   ctna0450
 * Simplyfied read/write handshaking
 *
 * /main/ms-d/60   2009-02-20 11:11:34   ctna0450
 * Bugfix from Intermec:
 * Increase thread priorities in Open() and not inside threads to allow threads to kick in even if system is busy
 *
 * /main/ms-d/59   2009-02-18 11:49:58   ctna0256
 * Set copyright text
 *
 * /main/ms-d/58   2009-02-02 10:54:13   ctna0152
 * changed debug messages
 *
 * /main/ms-d/57   2009-01-15 15:25:22   ctna0450
 * Added some traces
 *
 * /main/ms-d/56   2009-01-14 10:47:48   ctna0450
 * Make virtual port instancable to properly support handling of consecutive WaitCommEvent() calls
 *
 * /main/ms-d/55   2008-10-27 15:29:14   ctna0152
 * bugfix: calling getmodemstate() before line state is sent over USB from module
 *
 * /main/ms-d/54   2008-08-20 11:39:04   ctna0450
 * Prepared for new disclaimer handling
 *
 * /main/ms-d/53   2008-07-15 15:38:58   ht37010
 * bugfix: read out any received data even if already detached (happens if AT^SMSO is sent)
 *
 * /main/ms-d/52   2008-06-19 14:26:48   ht37010
 * added workarounf for missing events
 *
 * /main/ms-d/51   2008-04-11 19:09:47   ht37010
 * bugfix for CE-Swup Problem
 *
 * /main/ms-d/50   2008-04-10 15:15:33   ht37010
 * bugfix: deadlock in UsbPipe.WaitForResume() vs. VPort.Close()
 *
 * /main/ms-d/49   2008-03-03 12:43:37   ht37010
 * - added force flag to AbortTransfer() calls in Close() to abort wait-for-resmue
 * - corrected values returned in GetProperties()
 * - read QMI messages until no messages are available as bugfix for NDIS ftp upload problem in 3G networks
 * - increased timeouts from 100 to 500 ms
 * - bugfix: added type-cast in ThreadReadIrqData()
 * - handle NTF_CONNECTION_SPEED_CHG device request on CMD port
 * - added debug messages
 *
 * /main/ms-d/48   2007-12-18 14:55:07   ht37010
 *
 * /main/ms-d/47   2007-12-18 14:10:18   ht37010
 * changed debug zones
 *
 * /main/ms-d/46   2007-12-17 15:03:24   ht37010
 * new debug messages
 *
 * /main/ms-d/45   2007-09-12 12:52:14   ht37010
 * changed debug messages
 * added encapsulation for device request handling
 *
 * /main/ms-d/44   2007-09-07 15:08:40   ht37010
 * improved device request handling
 *
 * /main/ms-d/43   2007-08-31 13:50:27   ht37010
 * removed terminating of threads (not required anymore due to changes UsbHandling to to get threads runing again)
 *
 * /main/ms-d/42   2007-08-29 17:53:15   ht37010
 * terminate threads on Close() if thread does not exit on event
 *
 * /main/ms-d/41   2007-08-28 12:52:47   ht37010
 * added debug messages
 * added encapsulation for one-for-all driver
 *
 * /main/ms-d/40   2007-08-14 12:45:54   ht37010
 * added debug messages
 * added support for all modules
 *
 * /main/ms-d/39   2007-08-01 10:31:08   ht37010
 * improved handling if new device is attached before old device is detached during global resume
 *
 * /main/ms-d/38   2007-06-01 20:39:11   ht37010
 * removed critical section in CVirtualPort::IsPortOpen() to avoid deadlocks
 *
 * /main/ms-d/37   2007-05-30 16:33:47   ht37010
 * added functions for global validation of virtual port devices
 * wait forever for pipe trnasfer threads
 *
 * /main/ms-d/36   2007-04-12 13:41:58   ht37010
 * workaround for host reset on resume: release waiting event with extra function
 * added new debug messages
 *
 * /main/ms-d/35   2007-03-29 16:53:08   ht37010
 * fix: endless loop in ThreadReadData() / ThreadReadIrqData() if pipe handle gets invalid
 *
 * /main/ms-d/34   2007-03-28 17:22:10   KR36712
 * fix handle close event problem in ThreadReadData()
 *
 * /main/ms-d/33   2007-01-29 16:05:12   wm36264
 * Clearyfied names of some USB defines
 *
 * /main/ms-d/32   2006-12-22 15:28:52   wm36264
 * Added some ndis documentation
 *
 * /main/ms-d/31   2006-12-20 17:20:50   wm36264
 * Fixed problem in ndis read thread
 *
 * /main/ms-d/30   2006-12-20 17:09:17   wm36264
 * Implemented new ndis packet transfer mechanism (untested)
 *
 * /main/ms-d/29   2006-12-20 14:41:00   wm36264
 * Access QMI command port directly without read and write threads
 *
 * /main/ms-d/28   2006-12-18 16:45:40   wm36264
 * Create ndis port at last in case we haven'r enough pipes
 *
 * /main/ms-d/27   2006-12-13 15:48:46   wm36264
 * Fixed debug traces
 *
 * /main/ms-d/26   2006-12-13 15:17:26   wm36264
 * Properly leave data threads
 *
 * /main/ms-d/25   2006-12-06 11:08:39   wm36264
 * Reset interrupt pipe on all ports.
 * Make buffer for vendor transfers smaller.
 *
 * /main/ms-d/24   2006-11-16 12:41:40   wm36264
 * Properly close read and write thread on close
 *
 * /main/ms-d/23   2006-11-10 12:41:52   wm36264
 * Abort pending calls in case of USB failure
 *
 * /main/ms-d/22   2006-11-09 17:07:16   wm36264
 * Minor bugfixes
 *
 * /main/ms-d/21   2006-11-09 12:22:20   wm36264
 * Evaluate connection speed change notification
 *
 * /main/ms-d/20   2006-11-09 09:46:05   wm36264
 * Added useful debug zones
 *
 * /main/ms-d/19   2006-11-08 18:30:51   wm36264
 * Adjust USB transfer sizes to module requirements (fix strange problems with data connections)
 *
 * /main/ms-d/18   2006-11-02 17:33:16   wm36264
 * Added some traces
 *
 * /main/ms-d/17   2006-10-23 10:43:26   wm36264
 * Use global define for transfers on default pipe
 *
 * /main/ms-d/16   2006-10-23 10:21:27   wm36264
 * Don't block read transfers on default pipe forever
 *
 * /main/ms-d/15   2006-10-20 13:38:10   wm36264
 * Addapted to latest USB modifications in module
 *
 * /main/ms-d/14   2006-10-20 12:16:15   wm36264
 * The ndis irq pipe notifies us when there is qmi data available on the default pipe
 *
 * /main/ms-d/13   2006-10-18 12:33:54   wm36264
 * Several modifications regarding default pipe handling for qmi
 *
 * /main/ms-d/12   2006-10-13 17:23:02   wm36264
 * Removed test Sleep
 *
 * /main/ms-d/11   2006-10-13 17:10:30   wm36264
 * Fixed some debug traces
 *
 * /main/ms-d/10   2006-10-12 16:52:36   wm36264
 * For a possible future selective suspend implementation it is much easier when there is only one pipe class
 *
 * /main/ms-d/9   2006-10-10 15:28:29   wm36264
 * Use correct request types for encapsulated QMI commands
 *
 * /main/ms-d/8   2006-10-10 15:14:25   wm36264
 * Prepared usage of ndis interface number
 *
 * /main/ms-d/7   2006-10-09 15:24:10   wm36264
 * Fixed USB detach problems
 *
 * /main/ms-d/6   2006-10-06 18:00:27   wm36264
 * Make virtual ports fully instancable and use different device names for serial, ndis and qmi devices
 *
 * /main/ms-d/5   2006-09-29 15:33:16   wm36264
 * Consider latest findings about QMI and NDIS handling
 *
 * /main/ms-d/4   2006-09-22 14:33:19   wm36264
 * Cosmetics mostly
 *
 * /main/ms-d/3   2006-09-21 17:41:45   wm36264
 * No special treatment for NDIS port at this level
 *
 * /main/ms-d/2   2006-09-21 17:05:35   wm36264
 * Prepared access to QMI interface
 *
 * /main/ms-d/1   2006-09-18 15:31:06   wm36264
 * Initial Quinn Windows CE USB Driver based on UsbSer
 *
*/

//////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2008 Cinterion Wireless Modules GmbH
// Copyright (C) 2006-2008 Siemens AG
//
// Version: 1.0
// Date:    dummy
//
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//
// VirtualPort.cpp
//
// This file contains the implementation of the class "CVirtualPort" providing
// the functionality of the virtual COM port.
//
//////////////////////////////////////////////////////////////////////////////

#include "precomp.h"

//////////////////////////////////////////////////////////////////////////////
//
//  local variables
//
//////////////////////////////////////////////////////////////////////////////

CRITICAL_SECTION CVirtualPort::m_csSystemLock;

PointerArray CVirtualPort::m_PortList;

//////////////////////////////////////////////////////////////////////////////
//
// Public interface
//
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//
//////////////////////////////////////////////////////////////////////////////
HANDLE CVirtualPort::GetStreamHandle(void) {
  return m_hStreamHandle;
}

//////////////////////////////////////////////////////////////////////////////
//
//////////////////////////////////////////////////////////////////////////////
void CVirtualPort::SetStreamHandle(HANDLE hStreamHandle) {
  m_hStreamHandle = hStreamHandle;
}

//////////////////////////////////////////////////////////////////////////////
//
//////////////////////////////////////////////////////////////////////////////
BOOL CVirtualPort::SetUsbHandling(CUsbHandling *pUsbHandling) {
  m_pUsbHandling = pUsbHandling;
  return m_pUsbHandling->InitUsbInterface(this);
}

//////////////////////////////////////////////////////////////////////////////
//
//////////////////////////////////////////////////////////////////////////////
BOOL CVirtualPort::EnsurePortClosed(void) {
  DEBUGMSG(ZONE_USB, (TEXT("HcUsb: +CVirtualPort::EnsurePortClosed()\r\n")));
  BOOL fRet = (WaitForSingleObject(m_hPortClosedEvent, INFINITE) == WAIT_OBJECT_0) ? TRUE : FALSE;
  Sleep(100);
  DEBUGMSG(ZONE_USB, (TEXT("HcUsb: -CVirtualPort::EnsurePortClosed(%d)\r\n"), fRet));
  return fRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Initialize the critical section used to synchronize system functions.
//
// Parameters:
// None.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
void CVirtualPort::InitSystemLock(void) {
  DEBUGMSG(ZONE_INIT|ZONE_PORT, (TEXT("HcUsb: +CVirtualPort::InitSystemLock()\r\n")));
  InitializeCriticalSection(&m_csSystemLock);
  DEBUGMSG(ZONE_INIT|ZONE_PORT, (TEXT("HcUsb: -CVirtualPort::InitSystemLock()\r\n")));
}

//////////////////////////////////////////////////////////////////////////////
//
// Deinitialize the critical section used to synchronize system functions.
//
// Parameters:
// None.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
void CVirtualPort::DeinitSystemLock(void) {
  DEBUGMSG(ZONE_INIT|ZONE_PORT, (TEXT("HcUsb: +CVirtualPort::DeinitSystemLock()\r\n")));
  DeleteCriticalSection(&m_csSystemLock);
  DEBUGMSG(ZONE_INIT|ZONE_PORT, (TEXT("HcUsb: -CVirtualPort::DeinitSystemLock()\r\n")));
}

//////////////////////////////////////////////////////////////////////////////
//
// Enter the critical section used to synchronize system functions.
//
// Parameters:
// None.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
void CVirtualPort::LockSystem(void) {
  DEBUGMSG(ZONE_LOCK, (TEXT("HcUsb: +CVirtualPort::LockSystem()\r\n")));
  EnterCriticalSection(&m_csSystemLock);
  DEBUGMSG(ZONE_LOCK, (TEXT("HcUsb: -CVirtualPort::LockSystem()\r\n")));
}

//////////////////////////////////////////////////////////////////////////////
//
// Leave the critical section used to synchronize system functions.
//
// Parameters:
// None.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
void CVirtualPort::UnlockSystem(void) {
  DEBUGMSG(ZONE_LOCK, (TEXT("HcUsb: +CVirtualPort::UnlockSystem()\r\n")));
  LeaveCriticalSection(&m_csSystemLock);
  DEBUGMSG(ZONE_LOCK, (TEXT("HcUsb: -CVirtualPort::UnlockSystem()\r\n")));
}

//////////////////////////////////////////////////////////////////////////////
//
// Add a virtual port device to the global port device list.
//
// Parameters:
// virtPort: The virtual port device object to remove.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CVirtualPort::AddVirtualPort(CVirtualPort *virtPort) {
  DEBUGMSG(ZONE_INIT|ZONE_PORT, (TEXT("HcUsb: +CVirtualPort::AddVirtualPort(%p)\r\n"), virtPort));
  BOOL fRet = m_PortList.Add(virtPort);
  DEBUGMSG(ZONE_INIT|ZONE_PORT, (TEXT("HcUsb: -CVirtualPort::AddVirtualPort(%d)\r\n"), fRet));
  return fRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Removes a virtual port device from the global port device list.
//
// Parameters:
// virtPort: The virtual port device object to remove.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CVirtualPort::RemoveVirtualPort(CVirtualPort *virtPort) {
  DEBUGMSG(ZONE_INIT|ZONE_PORT, (TEXT("HcUsb: +CVirtualPort::RemoveVirtualPort(%p)\r\n"), virtPort));
  BOOL fRet = m_PortList.Remove(virtPort);
  DEBUGMSG(ZONE_INIT|ZONE_PORT, (TEXT("HcUsb: -CVirtualPort::RemoveVirtualPort(%d)\r\n"), fRet));
  return fRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Check if a virtual port device is still valid.
//
// Parameters:
// virtPort: The virtual port device object to check for validity.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CVirtualPort::IsVirtualPortValid(CVirtualPort *virtPort) {
  DEBUGMSG(ZONE_PORT, (TEXT("HcUsb: +CVirtualPort::IsVirtualPortValid(%p)\r\n"), virtPort));
  BOOL fRet = (m_PortList.Find(virtPort) >= 0) ? TRUE : FALSE;
  DEBUGMSG(ZONE_PORT, (TEXT("HcUsb: -CVirtualPort::IsVirtualPortValid(%d)\r\n"), fRet));
  return fRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Constructor
//
// Parameters:
// pUsbHandling: Pointer to the UsbHandling class instance for USB access
// DevType     : Device type of our instance.
//
//////////////////////////////////////////////////////////////////////////////
CVirtualPort::CVirtualPort(CUsbHandling* pUsbHandling, HC_DEVICE_TYPE DevType) {
  // Save pointer to UsbHandling class instance, device index and device type
  m_DevType = DevType;

  // Init internal resources
  InitializeCriticalSection(&m_csLock);
  m_dwOpenCount = 0;
  m_hThreadReadData = NULL;
  m_hThreadWriteData = NULL;
  m_hThreadReadIrqData = NULL;
  m_hCloseEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
  m_hPortClosedEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
  m_hCmdDataAvailableEvent = NULL;

  m_hStreamHandle = NULL;

  // Init the modem signals
  m_hLineStateValidEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
  m_fRing  = FALSE;
  m_fCts   = TRUE;
  m_fDsr   = FALSE;
  m_fCd    = FALSE;
  m_fRts   = FALSE;
  m_fDtr   = FALSE;

  // Init our internal DCB structure with useful values
  memset(&m_Dcb, 0, sizeof(m_Dcb));
  m_Dcb.DCBlength                       = sizeof(DCB);
  m_Dcb.BaudRate                        = CBR_115200;
  m_Dcb.fBinary                         = TRUE;
  m_Dcb.fParity                         = TRUE;
  m_Dcb.fOutxCtsFlow                    = TRUE;
  m_Dcb.fDtrControl                     = DTR_CONTROL_ENABLE;
  m_Dcb.fTXContinueOnXoff               = TRUE;
  m_Dcb.fRtsControl                     = RTS_CONTROL_HANDSHAKE;
  m_Dcb.ByteSize                        = 8;
  m_Dcb.Parity                          = NOPARITY;
  m_Dcb.StopBits                        = ONESTOPBIT;

  // Init our internal COMMTIMEOUTS structure with useful values
  memset(&m_Timeouts, 0, sizeof(m_Timeouts));
  m_Timeouts.ReadIntervalTimeout        = MAXDWORD;
  m_Timeouts.WriteTotalTimeoutConstant  = 3000;

  // Init the USB stuff
  m_pDefaultPipe= NULL;
  m_bUsbIfaceNumber = 0;
  m_bUsbPortCaps = 0;
  m_pUsbHandling = NULL;
  m_fUsbOk = SetUsbHandling(pUsbHandling);
  if (m_fUsbOk) {
    AddVirtualPort(this);
    CUsbDriver::AddActivatedPort(DevType, m_dwDeviceIndex, this, pUsbHandling);
  }
}

//////////////////////////////////////////////////////////////////////////////
//
// Destructor
//
//////////////////////////////////////////////////////////////////////////////
CVirtualPort::~CVirtualPort() {
  EnterCriticalSection(&m_csLock);
  Close(0);

  m_fUsbOk = FALSE;
  m_pUsbHandling->RemoveVirtualport(this);
  CUsbDriver::RemoveActivatedPort( this );
  RemoveVirtualPort( this );

  CloseHandle(m_hLineStateValidEvent);
  CloseHandle(m_hCloseEvent);
  CloseHandle(m_hPortClosedEvent);
  // Deinitialize the USB pipes
  if (m_DevType != HC_CMD) {
    m_BulkOutPipe.Deinitialize();
    m_BulkInPipe.Deinitialize();
    m_IrqInPipe.Deinitialize();
  } else  {
    CloseHandle(m_hCmdDataAvailableEvent);
  }
  LeaveCriticalSection(&m_csLock);
  DeleteCriticalSection(&m_csLock);
}

//////////////////////////////////////////////////////////////////////////////
//
// Open a virtual port device.
//
// Parameters:
// None.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CVirtualPort::Open(LPVOID pInstance) {
SERIAL_QUEUE_SIZES QueueSize;

  DEBUGMSG(ZONE_PORT, (TEXT("HcUsb: +CVirtualPort::Open()\r\n")));

  // No open possible without the USB handling instance
  if (!m_pUsbHandling) {
    DEBUGMSG(ZONE_PORT|ZONE_USB|ZONE_ERROR, (TEXT("HcUsb: -CVirtualPort::Open() - no USB available\r\n")));
    return FALSE;
  }

  // Deny port open if USB is not attached
  if (!m_pUsbHandling->IsUSBAttached()) {
    DEBUGMSG(ZONE_PORT|ZONE_USB|ZONE_ERROR, (TEXT("HcUsb: -CVirtualPort::Open() - already detached\r\n")));
    return FALSE;
  }

  DEBUGMSG(ZONE_LOCK, (TEXT("HcUsb:  CVirtualPort::Open() sync: +CVirtualPort %d\r\n"), m_bUsbIfaceNumber));
  SYNCBLOCK(m_csLock);
  DEBUGMSG(ZONE_LOCK, (TEXT("HcUsb:  CVirtualPort::Open() sync: -CVirtualPort %d\r\n"), m_bUsbIfaceNumber));
  // Also if something wrong with USB we deny the open
  if (!m_fUsbOk) {
    // if the port was destroy in between, the USB-OK flag will be cleared ...
    // continue with destruction now
    DEBUGMSG(ZONE_PORT|ZONE_USB|ZONE_ERROR, (TEXT("HcUsb: -CVirtualPort::Open(FALSE) - USB error\r\n")));
    return FALSE;
  }

  // No open possible without missing resources
  if (!m_hCloseEvent) {
    DEBUGMSG(ZONE_PORT|ZONE_ERROR, (TEXT("HcUsb: -CVirtualPort::Open(FALSE) - event error\r\n")));
    return FALSE;
  }

  // Check if we are not already open
  if (m_dwOpenCount) {
    m_dwOpenCount++;
    m_WaitCommEvent.AddInstance(pInstance);
    DEBUGMSG(ZONE_PORT, (TEXT("HcUsb: -CVirtualPort::Open(TRUE) - already opened\r\n")));
    return TRUE;
  }

  // Create cmd available event
  if (m_DevType == HC_CMD) {
    m_hCmdDataAvailableEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    if (!m_hCmdDataAvailableEvent) {
      DEBUGMSG(ZONE_USB|ZONE_ERROR, (TEXT("HcUsb: -CVirtualPort::Open(FALSE) - data event error\r\n")));
      return FALSE;
    }
  }

  // Reset our internal notification events
  ResetEvent(m_hCloseEvent);
  ResetEvent(m_hPortClosedEvent);

  // Increase counter for open instances
  m_dwOpenCount++;

  // Initialize read and write buffer
  QueueSize.cbInQueue = DEFAULT_BUFFER_SIZE;
  QueueSize.cbOutQueue = DEFAULT_BUFFER_SIZE;
  if (!SetQueueSize(&QueueSize)) {
    SetEvent(m_hCloseEvent);
    SetEvent(m_hPortClosedEvent);
    if (m_hCmdDataAvailableEvent) {
      CloseHandle(m_hCmdDataAvailableEvent);
      m_hCmdDataAvailableEvent = NULL;
    }
    m_dwOpenCount--;
    DEBUGMSG(ZONE_USB|ZONE_ERROR, (TEXT("HcUsb: -CVirtualPort::Open(FALSE) - buffer error\r\n")));
    return FALSE;
  }

  // Open all pipes
  if (m_DevType != HC_CMD) {
    m_BulkOutPipe.Open();
    m_BulkInPipe.Open();
  } else {
    m_pDefaultPipe->Open();
  }
  if (m_IrqInPipe.IsValid()) {
    m_IrqInPipe.Open();
  }

  // Reset the connected pipes
  if (!ResetPipes()) {
    SetEvent(m_hCloseEvent);
    SetEvent(m_hPortClosedEvent);
    if (m_hCmdDataAvailableEvent) {
      CloseHandle(m_hCmdDataAvailableEvent);
      m_hCmdDataAvailableEvent = NULL;
    }
    m_dwOpenCount--;
    DEBUGMSG(ZONE_USB | ZONE_PORT | ZONE_ERROR, (TEXT("HcUsb:  CVirtualPort::Open() pipe reset failed\r\n")));
    return FALSE;
  }

  // Init our virtual modem signals
  m_fRts = FALSE;
  m_fDtr = FALSE;
  if (m_Dcb.fRtsControl != RTS_CONTROL_DISABLE) m_fRts = TRUE;
  if (m_Dcb.fDtrControl != DTR_CONTROL_DISABLE) m_fDtr = TRUE;
  if (IsSerial()) {
    USHORT wValue = 0;
    if (m_fRts) wValue |= LINE_STATE_RTS_MASK;
    if (m_fDtr) wValue |= LINE_STATE_DTR_MASK;
    DEBUGMSG(ZONE_PORT|ZONE_USB, (TEXT("HcUsb:  CVirtualPort::Open() - init modem lines\r\n")));
    if (m_pDefaultPipe->TransferVendorData(TRUE, m_bUsbIfaceNumber, REQ_SET_CONTROL_LINE_STATE, wValue, NULL, 0, DEFAULT_PIPE_TIMEOUT) == -1) {
      SetEvent(m_hCloseEvent);
      SetEvent(m_hPortClosedEvent);
      if (m_hCmdDataAvailableEvent) {
        CloseHandle(m_hCmdDataAvailableEvent);
        m_hCmdDataAvailableEvent = NULL;
      }
      m_dwOpenCount--;
      DEBUGMSG(ZONE_USB, (TEXT("HcUsb: -CVirtualPort::Open(FALSE) - modem line error\r\n")));
      return FALSE;
    }
  }

  if (m_DevType != HC_CMD) {
    // Create read data thread
    m_hThreadReadData = CreateThread(NULL, 0, ThreadReadData, this, 0, NULL);
    if (!m_hThreadReadData) {
      SetEvent(m_hCloseEvent);
      SetEvent(m_hPortClosedEvent);
      if (m_hCmdDataAvailableEvent) {
        CloseHandle(m_hCmdDataAvailableEvent);
        m_hCmdDataAvailableEvent = NULL;
      }
      m_dwOpenCount--;
      DEBUGMSG(ZONE_PORT | ZONE_ERROR, (TEXT("HcUsb:  CVirtualPort::Open() create ReadThread failed\r\n")));
      return FALSE;
    }

    // Create write data thread
    m_hThreadWriteData = CreateThread(NULL, 0, ThreadWriteData, this, 0, NULL);
    if (!m_hThreadWriteData) {
      SetEvent(m_hCloseEvent);
      SetEvent(m_hPortClosedEvent);
      AbortWrite();
      WaitForSingleObject(m_hThreadReadData, 1000);
      CloseHandle(m_hThreadReadData);
      m_hThreadReadData = NULL;
      if (m_hCmdDataAvailableEvent) {
        CloseHandle(m_hCmdDataAvailableEvent);
        m_hCmdDataAvailableEvent = NULL;
      }
      m_dwOpenCount--;
      DEBUGMSG(ZONE_PORT | ZONE_ERROR, (TEXT("HcUsb:  CVirtualPort::Open() create write thread failed\r\n")));
      return FALSE;
    }

    // Increase priority of read thread to prioritize incoming data
    CeSetThreadPriority(m_hThreadReadData, 100);
    // Decrease priority of write thread. That allows outgoing data packets to pile up
    // in the ring buffer and that improves USB throughput in case of consecutive
    // small packets.
    CeSetThreadPriority(m_hThreadWriteData, 200);
  }

  // Create read irq data thread
  if (m_IrqInPipe.IsValid()) {
    m_hThreadReadIrqData = CreateThread(NULL, 0, ThreadReadIrqData, this, 0, NULL);
    if (!m_hThreadReadIrqData) {
      SetEvent(m_hCloseEvent);
      SetEvent(m_hPortClosedEvent);
      AbortWrite();
      AbortRead();
      if (m_hThreadReadData) {
        WaitForSingleObject(m_hThreadReadData, 1000);
        CloseHandle(m_hThreadReadData);
        m_hThreadReadData = NULL;
      }
      if (m_hThreadWriteData) {
        WaitForSingleObject(m_hThreadWriteData, 1000);
        CloseHandle(m_hThreadWriteData);
        m_hThreadWriteData = NULL;
      }
      if (m_hCmdDataAvailableEvent) {
        CloseHandle(m_hCmdDataAvailableEvent);
        m_hCmdDataAvailableEvent = NULL;
      }
      m_dwOpenCount--;
      DEBUGMSG(ZONE_PORT | ZONE_ERROR, (TEXT("HcUsb:  CVirtualPort::Open() create IRW read thread failed\r\n")));
      return FALSE;
    }

    // Increase thread priority to prioritize irq data
    CeSetThreadPriority(m_hThreadReadIrqData, 100);
  }

  // Let the USB handler know that we've opened the port instance
  if (!m_pUsbHandling->AddOpenedPort()) {
    SetEvent(m_hCloseEvent);
    SetEvent(m_hPortClosedEvent);
    AbortWrite();
    AbortRead();
    if (m_hThreadReadData) {
      WaitForSingleObject(m_hThreadReadData, 1000);
      CloseHandle(m_hThreadReadData);
      m_hThreadReadData = NULL;
    }
    if (m_hThreadWriteData) {
      WaitForSingleObject(m_hThreadWriteData, 1000);
      CloseHandle(m_hThreadWriteData);
      m_hThreadWriteData = NULL;
    }
    if (m_hThreadReadIrqData) {
      m_IrqInPipe.AbortTransfer();
      WaitForSingleObject(m_hThreadReadIrqData, 1000);
      CloseHandle(m_hThreadReadIrqData);
      m_hThreadReadIrqData = NULL;
    }
    if (m_hCmdDataAvailableEvent) {
      CloseHandle(m_hCmdDataAvailableEvent);
      m_hCmdDataAvailableEvent = NULL;
    }
    m_dwOpenCount--;
    DEBUGMSG(ZONE_PORT | ZONE_INIT | ZONE_ERROR, (TEXT("HcUsb:  CVirtualPort::Open() adding new port failed\r\n")));
    return FALSE;
  }

  m_WaitCommEvent.AddInstance(pInstance);

  return TRUE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Close a virtual port device.
//
// Parameters:
// fForce: Force resources to be freed.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CVirtualPort::Close(LPVOID pInstance) {
  DEBUGMSG(ZONE_PORT, (TEXT("HcUsb: +CVirtualPort::Close()\r\n")));

  DEBUGMSG(ZONE_LOCK, (TEXT("HcUsb:  CVirtualPort::Close() - sync: +CVirtualPort %p\r\n"), this));
  SYNCBLOCK(m_csLock);
  DEBUGMSG(ZONE_LOCK, (TEXT("HcUsb:  CVirtualPort::Close() - sync: -CVirtualPort %p\r\n"), this));

  // Check if we are really open
  if (!m_dwOpenCount) {
    DEBUGMSG(ZONE_PORT, (TEXT("HcUsb: -CVirtualPort::Close(FALSE) - never opened\r\n")));
    return FALSE;
  }

  // Release pending WaitCommEvent() calls
  m_WaitCommEvent.RemoveInstance(pInstance);

  // Tidy up only if the last instance is closed
  if ((m_dwOpenCount == 1) || !pInstance) {
    // Signal the close event and abort all pending waits
    DEBUGMSG(ZONE_PORT & ZONE_INFO, (TEXT("HcUsb:  CVirtualPort::Close() - set close event\r\n")));
    SetEvent(m_hCloseEvent);

    // Reset ring buffers
    m_ReadBuffer.Resize(0);
    m_WriteBuffer.Resize(0);

    DEBUGMSG(ZONE_PORT & ZONE_INFO, (TEXT("HcUsb:  CVirtualPort::Close() - abort transfers\r\n")));

    // Abort running USB transfers
    if (m_DevType == HC_CMD) {
      m_pDefaultPipe->AbortTransfer();
    } else {
      m_BulkInPipe.AbortTransfer();
      m_BulkOutPipe.AbortTransfer();
    }

    // Stop the read irq data thread
    if (m_hThreadReadIrqData) {
      m_IrqInPipe.AbortTransfer();
      {
        DEBUGMSG(ZONE_PORT & ZONE_INFO, (TEXT("HcUsb:  CVirtualPort::Close() - wait for termination of IRQ thread\r\n")));
        UNSYNCBLOCK(m_csLock);
        WaitForSingleObject(m_hThreadReadIrqData, INFINITE);
      }
      CloseHandle(m_hThreadReadIrqData);
      m_hThreadReadIrqData = NULL;
    }

    // Stop the read data thread
    if (m_hThreadReadData) {
      {
        DEBUGMSG(ZONE_PORT & ZONE_INFO, (TEXT("HcUsb:  CVirtualPort::Close() - wait for termination of RX thread\r\n")));
        UNSYNCBLOCK(m_csLock);
        WaitForSingleObject(m_hThreadReadData, INFINITE);
      }
      CloseHandle(m_hThreadReadData);
      m_hThreadReadData = NULL;
    }

    // Stop the write data thread
    if (m_hThreadWriteData) {
      {
        DEBUGMSG(ZONE_PORT & ZONE_INFO, (TEXT("HcUsb:  CVirtualPort::Close() - wait for termination of TX thread\r\n")));
        UNSYNCBLOCK(m_csLock);
        WaitForSingleObject(m_hThreadWriteData, INFINITE);
      }
      CloseHandle(m_hThreadWriteData);
      m_hThreadWriteData = NULL;
    }

    DEBUGMSG(ZONE_PORT & ZONE_INFO, (TEXT("HcUsb:  CVirtualPort::Close() - release wait events\r\n")));

    // Abort pending ndis calls
    if (m_DevType == HC_NDIS) {
      DEBUGMSG(ZONE_PORT|ZONE_USB, (TEXT("HcUsb:  CVirtualPort::Close() - abort NDIS calls\r\n")));

      PHCCMD_NDIS_DATA pNdisData;
      while (m_ReadBuffer.Read((PBYTE)(&pNdisData), sizeof(PHCCMD_NDIS_DATA)) == sizeof(PHCCMD_NDIS_DATA)) {
        pNdisData->dwTransferred = (DWORD)-1;
        SetEvent(pNdisData->hFinishEvent);
      }
      while (m_WriteBuffer.Read((PBYTE)(&pNdisData), sizeof(PHCCMD_NDIS_DATA)) == sizeof(PHCCMD_NDIS_DATA)) {
        pNdisData->dwTransferred = (DWORD)-1;
        SetEvent(pNdisData->hFinishEvent);
      }
    }

    m_dwOpenCount = 0;

    // Let the USB handler know that we've closed the port instance
    if (!m_pUsbHandling->RemoveOpenedPort()) {
      DEBUGMSG(ZONE_PORT|ZONE_ERROR, (TEXT("HcUsb: -CVirtualPort::Close(FALSE) - remove failed\r\n")));
      return FALSE;
    }

    if (m_hCmdDataAvailableEvent) {
      CloseHandle(m_hCmdDataAvailableEvent);
      m_hCmdDataAvailableEvent = NULL;
    }

    // Close all pipes
    if (m_DevType != HC_CMD) {
      m_BulkOutPipe.Close();
      m_BulkInPipe.Close();
    } else {
      m_pDefaultPipe->Close();
    }
    if (m_IrqInPipe.IsValid()) {
      m_IrqInPipe.Close();
    }

    SetEvent(m_hPortClosedEvent);

    DEBUGMSG(ZONE_USB, (TEXT("HcUsb: -CVirtualPort::Close(TRUE)\r\n")));
    return TRUE;
  }

  m_dwOpenCount--;
  DEBUGMSG(ZONE_USB, (TEXT("HcUsb: -CVirtualPort::Close(TRUE) - OpenCount=%d\r\n"), m_dwOpenCount));

  return TRUE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Copy the internal used DCB structure into an external buffer.
//
// Parameters:
// pDst: The buffer where we should copy our DCB structure to.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CVirtualPort::GetDcb(DCB* pDst) {
  SYNCBLOCK(m_csLock);
  if (!IsPortOpen()) return FALSE;
  memcpy(pDst, &m_Dcb, sizeof(DCB));
  return TRUE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Copy an external DCB structure into the internal buffer and use the given
// values.
//
// Parameters:
// pDst: The buffer where we should copy the DCB structure from.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CVirtualPort::SetDcb(DCB* pSrc) {
  SYNCBLOCK(m_csLock);
  if (!IsPortOpen()) return FALSE;
  memcpy(&m_Dcb, pSrc, sizeof(DCB));
  return TRUE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Copy the internal used COMMTIMEOUTS structure into an external buffer.
//
// Parameters:
// pDst: The buffer where we should copy our DCB structure to.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CVirtualPort::GetTimeouts(COMMTIMEOUTS* pDst) {
  SYNCBLOCK(m_csLock);
  if (!m_dwOpenCount) return FALSE;
  memcpy(pDst, &m_Timeouts, sizeof(COMMTIMEOUTS));
  return TRUE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Copy an external COMMTIMEOUTS structure into the internal buffer and use the given
// values.
//
// Parameters:
// pDst: The buffer where we should copy the DCB structure from.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CVirtualPort::SetTimeouts(COMMTIMEOUTS* pSrc) {
  SYNCBLOCK(m_csLock);
  if (!IsPortOpen()) return FALSE;
  memcpy(&m_Timeouts, pSrc, sizeof(COMMTIMEOUTS));
  return TRUE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Get the current WaitCommEvent() mask.
//
// Parameters:
// pdwWaitMask: The buffer receiving the current wait mask.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CVirtualPort::GetWaitMask(LPVOID pInstance, PDWORD pdwWaitMask) {
  if (!IsPortOpen()) return FALSE;
  return m_WaitCommEvent.GetWaitMask(pInstance, pdwWaitMask);
}

//////////////////////////////////////////////////////////////////////////////
//
// Set the WaitCommEvent() mask. The masked is ored with the existing mask to
// properly handle consecutive calls. If the mask is set to zero all pending
// WaitCommEvent() calls are released.
//
// Parameters:
// dwWaitMask: The new wait mask.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CVirtualPort::SetWaitMask(LPVOID pInstance, DWORD dwWaitMask) {
BOOL fRet;

  DEBUGMSG(ZONE_PORT_EVENTS, (TEXT("HcUsb: +CVirtualPort::SetWaitMask(this=%p, instance=%d, mask=%x)\r\n"), this, pInstance, dwWaitMask));
  if (!IsPortOpen()) {
    // Release pending WaitCommEvent() calls in any case
    m_WaitCommEvent.SetWaitMask(pInstance, 0);
    DEBUGMSG(ZONE_PORT_EVENTS | ZONE_ERROR, (TEXT("HcUsb: -CVirtualPort::SetWaitMask(FALSE) - port not open\r\n")));
    return FALSE;
  }
  // Abort possibly pending read transfer if wait mask is reset
  if (!dwWaitMask) m_ReadBuffer.AbortWaitForData();
  fRet = m_WaitCommEvent.SetWaitMask(pInstance, dwWaitMask);
  DEBUGMSG(ZONE_PORT_EVENTS, (TEXT("HcUsb: -CVirtualPort::SetWaitMask(this=%p, instance=%d) -> %d\r\n"), this, pInstance, fRet));
  return fRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Wait for COM events fitting to the COM mask. The function blocks until
// COM event occured or wait mask is set to zero.
//
// Parameters:
// pdwWaitEvent: The COM event caused the release of the function call.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CVirtualPort::WaitOnMask(LPVOID pInstance, PDWORD pdwWaitEvent) {
BOOL fRet;

  DEBUGMSG(ZONE_PORT_EVENTS, (TEXT("HcUsb: +CVirtualPort::WaitOnMask(this=%p, instance=0x%02X)\r\n"), this, pInstance));
  if (!IsPortOpen()) {
    DEBUGMSG(ZONE_PORT_EVENTS | ZONE_ERROR, (TEXT("HcUsb: -CVirtualPort::WaitOnMask(0) port is NOT open!\r\n")));
    return FALSE;
  }
  fRet = m_WaitCommEvent.WaitForEvent(pInstance, pdwWaitEvent);
  DEBUGMSG(ZONE_PORT_EVENTS, (TEXT("HcUsb: -CVirtualPort::WaitOnMask(this=%p, instance=%p, event=%x) -> %d\r\n"), this, pInstance, *pdwWaitEvent, fRet));
  return fRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Get the properties of the virtual port.
//
// Parameters:
// pDst: The buffer where we should copy our COMMPROP structure to.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CVirtualPort::GetProperties(COMMPROP* pDst) {
  if (!IsPortOpen()) return FALSE;

  memset(pDst, 0, sizeof(COMMPROP));

  pDst->wPacketLength        =  MAXWORD;
  pDst->wPacketVersion       =  MAXWORD;
  pDst->dwServiceMask        =  SP_SERIALCOMM;
  pDst->dwMaxTxQueue         =  m_WriteBuffer.GetSize();
  pDst->dwMaxRxQueue         =  m_ReadBuffer.GetSize();
  pDst->dwCurrentTxQueue     =  m_WriteBuffer.GetLevel();
  pDst->dwCurrentRxQueue     =  m_ReadBuffer.GetLevel();
  pDst->dwMaxBaud            =  BAUD_USER;
  pDst->dwProvSubType        =  PST_RS232;
  pDst->dwProvCapabilities   =  PCF_DTRDSR | PCF_RTSCTS | PCF_RLSD | PCF_TOTALTIMEOUTS | PCF_INTTIMEOUTS;
  pDst->dwSettableParams     =  SP_HANDSHAKING | SP_RLSD;
  pDst->dwSettableBaud       =  BAUD_USER;
  pDst->wSettableData        =  DATABITS_5 | DATABITS_6 | DATABITS_7 | DATABITS_8;
  pDst->wSettableStopParity  =  PARITY_NONE | PARITY_ODD  | PARITY_EVEN | PARITY_MARK | PARITY_SPACE |
                                STOPBITS_10 | STOPBITS_15 | STOPBITS_20;
  return TRUE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Set the size of the read and write queues of the virtual port.
// The buffers consist of two levels. The first ones are the ring buffers
// where the data is stored directly by ReadFile() and WriteFile()
// operations. The second ones are the buffers used directly for the USB
// read and write operations emptying the ring buffers again. Because the two
// buffer levels share the data passed to the driver the buffer sizes are
// split to these two levels.
//
// Parameters:
// pQueueSize: The new queue sizes.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CVirtualPort::SetQueueSize(SERIAL_QUEUE_SIZES* pQueueSize) {
BOOL fRet = TRUE;

  DEBUGMSG(ZONE_PORT, (TEXT("HcUsb: +CVirtualPort::SetQueueSize()\r\n")));

  DEBUGMSG(ZONE_LOCK, (TEXT("HcUsb:  CVirtualPort::SetQueueSize() sync: +CVirtualPort %d\r\n"), m_bUsbIfaceNumber));
  SYNCBLOCK(m_csLock);
  DEBUGMSG(ZONE_LOCK, (TEXT("HcUsb:  CVirtualPort::SetQueueSize() sync: -CVirtualPort %d\r\n"), m_bUsbIfaceNumber));

  if (!IsPortOpen()) return FALSE;

  if (m_DevType == HC_CMD) return TRUE;

  // Resize the read buffer
  if (!m_ReadBuffer.Resize((max(pQueueSize->cbInQueue, USB_TRANSFER_SIZE) >> 2) << 2)) {
    fRet = FALSE;
  }

  // Resize the write buffer
  if (!m_WriteBuffer.Resize((max(pQueueSize->cbOutQueue, USB_TRANSFER_SIZE) >> 2) << 2)) {
    fRet = FALSE;
  }

  DEBUGMSG(ZONE_PORT, (TEXT("HcUsb: -CVirtualPort::SetQueueSize(%s)\r\n"),fRet?TEXT("TRUE"):TEXT("FALSE")));
  return fRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Get the virtual port status information.
//
// Parameters:
// pDst: The buffer where we should copy our COMSTAT structure to.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CVirtualPort::GetComStat(COMSTAT* pDst) {
  if (!IsPortOpen()) return FALSE;
  memset(pDst, 0, sizeof(COMSTAT));
  pDst->cbInQue = m_ReadBuffer.GetLevel();
  pDst->cbOutQue = m_WriteBuffer.GetLevel();
  return TRUE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Set the virtual RTS signal to low or high.
//
// Parameters:
// fState: Flag indicating if we should set (TRUE) or clear (FALSE) the
//         virtual RTS signal.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CVirtualPort::SetRts(BOOL fState) {
  SYNCBLOCK(m_csLock);
  if (!IsPortOpen()) return FALSE;
  if (!IsSerial()) return FALSE;
  if (m_Dcb.fRtsControl == RTS_CONTROL_HANDSHAKE) return FALSE;
  if (m_fRts == fState) return TRUE;
  m_fRts = fState;
  USHORT wValue = 0;
  if (m_fRts) wValue |= LINE_STATE_RTS_MASK;
  if (m_fDtr) wValue |= LINE_STATE_DTR_MASK;
  UNSYNCBLOCK(m_csLock);
  if (m_pDefaultPipe->TransferVendorData(TRUE, m_bUsbIfaceNumber, REQ_SET_CONTROL_LINE_STATE, wValue, NULL, 0, DEFAULT_PIPE_TIMEOUT) == -1) return FALSE;
  return TRUE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Set the virtual DTR signal to low or high.
//
// Parameters:
// fState: Flag indicating if we should set (TRUE) or clear (FALSE) the
//         virtual DTR signal.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CVirtualPort::SetDtr(BOOL fState) {
  SYNCBLOCK(m_csLock);
  if (!IsPortOpen()) return FALSE;
  if (!IsSerial()) return FALSE;
  if (m_Dcb.fDtrControl == DTR_CONTROL_HANDSHAKE) return FALSE;
  if (m_fDtr == fState) return TRUE;
  m_fDtr = fState;
  USHORT wValue = 0;
  if (m_fRts) wValue |= LINE_STATE_RTS_MASK;
  if (m_fDtr) wValue |= LINE_STATE_DTR_MASK;
  UNSYNCBLOCK(m_csLock);
  if (m_pDefaultPipe->TransferVendorData(TRUE, m_bUsbIfaceNumber, REQ_SET_CONTROL_LINE_STATE, wValue, NULL, 0, DEFAULT_PIPE_TIMEOUT) == -1) return FALSE;
  return TRUE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Set the virtual break state to on or off.
//
// Parameters:
// fState: Flag indicating if we should set (TRUE) or clear (FALSE) the
//         virtual break state.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CVirtualPort::SetBreak(BOOL fState) {
  if (!IsPortOpen()) return FALSE;

  if (!IsSerial()) {
    // Break only possible on serial port devices
    SetLastError(ERROR_NOT_SUPPORTED);
    return FALSE;
  }

  if (!(m_bUsbPortCaps & CAPS_SEND_BREAK)) {
    // Break is not supported by the device
    SetLastError(ERROR_NOT_SUPPORTED);
    return FALSE;
  }

  if (m_pDefaultPipe->TransferVendorData(TRUE, m_bUsbIfaceNumber, REQ_SEND_BREAK, fState ? 0xFFFF : 0x0000, NULL, 0, DEFAULT_PIPE_TIMEOUT) == -1) return FALSE;

  return TRUE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Get the current modem state with the modem control signals.
//
// Parameters:
// pdwState: The buffer receiving the current modem state.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CVirtualPort::GetModemState(PDWORD pdwState) {
  if (!IsPortOpen()) return FALSE;
  if ( WaitForSingleObject( m_hLineStateValidEvent, 0 ) != WAIT_OBJECT_0 ) {
    DEBUGMSG(ZONE_ERROR, (TEXT("HcUsb:  CVirtualPort::GetModemState() - wait for line state\r\n")));

    if ( WaitForSingleObject( m_hLineStateValidEvent, 3000 ) != WAIT_OBJECT_0 ) {
      SetLastError(ERROR_NOT_READY);
      return FALSE;
    }
  }

  SYNCBLOCK(m_csLock);
  *pdwState = 0;
  if (m_fRing)  *pdwState |= MS_RING_ON;
  if (m_fCts)   *pdwState |= MS_CTS_ON;
  if (m_fDsr)   *pdwState |= MS_DSR_ON;
  if (m_fCd)    *pdwState |= MS_RLSD_ON;
  return TRUE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Abort possible ongoing write operation.
//
// Parameters:
// None.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CVirtualPort::AbortWrite(void) {
  if (!IsPortOpen()) return FALSE;
  if (m_DevType == HC_CMD) {
    return m_pDefaultPipe->AbortTransfer();
  }
  return m_BulkOutPipe.AbortTransfer();
}

//////////////////////////////////////////////////////////////////////////////
//
// Abort possible ongoing read operation.
//
// Parameters:
// None.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CVirtualPort::AbortRead(void) {
  if (!IsPortOpen()) return FALSE;
  if (m_DevType == HC_CMD) {
    return m_pDefaultPipe->AbortTransfer();
  }
  return m_BulkInPipe.AbortTransfer();
}

//////////////////////////////////////////////////////////////////////////////
//
// Clear the write buffer.
//
// Parameters:
// None.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CVirtualPort::ClearWrite(void) {
  if (!IsPortOpen()) return FALSE;
  if (m_DevType == HC_CMD) return TRUE;
  return m_WriteBuffer.Empty();
}

//////////////////////////////////////////////////////////////////////////////
//
// Clear the read buffer.
//
// Parameters:
// None.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CVirtualPort::ClearRead(void) {
  if (!IsPortOpen()) return FALSE;
  if (m_DevType == HC_CMD) return TRUE;
  return m_ReadBuffer.Empty();
}

//////////////////////////////////////////////////////////////////////////////
//
// Read data from the virtual USB port.
//
// Parameters:
// pData: The buffer receiving the data. In case of an ndis port there is a
//        pointer to a "HCCMD_NDIS_DATA" structure containing the buffer
//        passed. The virtual port is handling these pointers in it's internal
//        ring buffer for later reading. If the read has finished the
//        hFinishEvent included in the structure will be set. For handling of
//        these ndis buffers the application is responsible.
// dwLen: The max. number of bytes to read. In case of a ndis port there is
//        only a pointer given, so the size should be 4.

//
// Return:
// The number of actually read bytes, -1 indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
DWORD CVirtualPort::ReadData(PBYTE pData, DWORD dwLen) {
BOOL    fReturnAfterAnyByte;
DWORD   dwTimeout;
DWORD   dwRead;
DWORD   dwTransferred;
DWORD   dwTime;

  DEBUGMSG(ZONE_PORT_READ, (TEXT("HcUsb: +CVirtualPort::ReadData(this=%p,dat=%p,len=%d) on IF %d\r\n"), this, pData, dwLen, m_bUsbIfaceNumber));

  SYNCBLOCK(m_csLock);

  // Check input parameter
  if (!pData || !dwLen) {
    SetLastError(ERROR_INVALID_PARAMETER);
    DEBUGMSG(ZONE_PORT_READ | ZONE_ERROR, (TEXT("HcUsb: -CVirtualPort::ReadData(-1) - Invalid parameter\r\n")));
    return (DWORD)-1;
  }

  if (!IsPortOpen()) {
    if (m_ReadBuffer.GetLevel() > 0) {
      DEBUGMSG(ZONE_PORT_READ, (TEXT("HcUsb: CVirtualPort::ReadData() - Port not open, but data in rx buffer\r\n")));
      // continue with read request ...
    } else {
      DEBUGMSG(ZONE_PORT_READ | ZONE_ERROR, (TEXT("HcUsb: -CVirtualPort::ReadData(-1) - Port not open\r\n")));
      return (DWORD)-1;
    }
  }

  // Calculate the timeout to be used
  fReturnAfterAnyByte = FALSE;
  if ((m_Timeouts.ReadIntervalTimeout == MAXDWORD) && !m_Timeouts.ReadTotalTimeoutConstant && !m_Timeouts.ReadTotalTimeoutMultiplier) {
    // Documented special handling:
    // A value of MAXDWORD for ReadIntervalTimeout, combined with zero values for both the
    // ReadTotalTimeoutConstant and ReadTotalTimeoutMultiplier members, specifies that the
    // read operation is to return immediately with the characters that have already been
    // received, even if no characters have been received.
    dwTimeout = 0;
    fReturnAfterAnyByte = TRUE;
  } else if ((m_Timeouts.ReadIntervalTimeout == MAXDWORD) && (m_Timeouts.ReadTotalTimeoutMultiplier == MAXDWORD) &&
             (m_Timeouts.ReadTotalTimeoutConstant != MAXDWORD) && m_Timeouts.ReadTotalTimeoutConstant) {
    // Documented special handling:
    // If an application sets ReadIntervalTimeout and ReadTotalTimeoutMultiplier to
    // MAXDWORD and sets ReadTotalTimeoutConstant to a value greater than zero and
    // less than MAXDWORD, one of the following occurs when the ReadFile function
    // is called:
    //  - If there are any characters in the input buffer, ReadFile returns
    //    immediately with the characters in the buffer.
    //  - If there are no characters in the input buffer, ReadFile waits until a
    //    character arrives and then returns immediately.
    //  - If no characters arrive within the time specified by
    //    ReadTotalTimeoutConstant, ReadFile times out.
    dwTimeout = m_Timeouts.ReadTotalTimeoutConstant;
    fReturnAfterAnyByte = TRUE;
  } else {
    // We've catched the special cases, now we can calculate the given timeouts
    if ((m_Timeouts.ReadTotalTimeoutConstant == MAXDWORD) || (m_Timeouts.ReadTotalTimeoutMultiplier == MAXDWORD)) {
      dwTimeout = INFINITE;
    } else {
      dwTimeout = (m_Timeouts.ReadTotalTimeoutMultiplier * dwLen) + m_Timeouts.ReadTotalTimeoutConstant;
    }
  }

  UNSYNCBLOCK(m_csLock);

  if (m_DevType == HC_CMD) {
    HANDLE hEvents[] = { m_hCloseEvent, m_hCmdDataAvailableEvent };
    dwTransferred = (DWORD)-1;

    if (WaitForMultipleObjects(2, hEvents, FALSE, dwTimeout) == (WAIT_OBJECT_0 + 1)) {
      dwTransferred = m_pDefaultPipe->TransferVendorData(FALSE, m_bUsbIfaceNumber, REQ_GET_ENCAPSULATED_RSP, 0, pData, (USHORT)dwLen, DEFAULT_PIPE_TIMEOUT);
      if (dwTransferred != (DWORD)-1) {
        DEBUGMSG(ZONE_PORT_READ | ZONE_PORT_DATA, (TEXT("HcUsb:  CVirtualPort::ReadData() - Read %d bytes on cmd port\r\n"), dwTransferred));
        DEBUGHEX(ZONE_PORT_DATA, TEXT("HcUsb:"), pData, dwTransferred);
        if (dwTransferred == 0) {
          // wait for data event ...
          DEBUGMSG(ZONE_PORT_DATA, (TEXT("HcUsb:  CVirtualPort::ReadData() - TransferVendorData() on cmd port wait for event\r\n")));
        } else {
          // set event to continue reading responses
          SetEvent(m_hCmdDataAvailableEvent);
        }
      } else {
        DEBUGMSG(ZONE_ERROR, (TEXT("HcUsb:  CVirtualPort::ReadData() - Failed: TransferVendorData() on cmd port\r\n")));
      }
    }
  } else {
    if (fReturnAfterAnyByte) {
      if (m_DevType == HC_NDIS) {
        DEBUGMSG(ZONE_PORT_DATA, (TEXT("HcUsb:  CVirtualPort::ReadData() - read NDIS data\r\n")));
        m_ReadBuffer.WaitForSpace(dwTimeout);
        dwTransferred = m_ReadBuffer.Write((PBYTE)(&pData), sizeof(PHCCMD_NDIS_DATA));
      } else {
        m_ReadBuffer.WaitForData(dwTimeout);
        dwTransferred = m_ReadBuffer.Read(pData, dwLen);
      }
    } else {
      dwTransferred = 0;
      do {
        if (m_DevType == HC_NDIS) {
          DEBUGMSG(ZONE_PORT_DATA, (TEXT("HcUsb:  CVirtualPort::ReadData() - read NDIS data\r\n")));
          dwTime = m_ReadBuffer.WaitForSpace(dwTimeout);
          dwRead = m_ReadBuffer.Write((PBYTE)(&pData), sizeof(PHCCMD_NDIS_DATA));
        } else {
          dwTime = m_ReadBuffer.WaitForData(dwTimeout);
          dwRead = m_ReadBuffer.Read(pData, dwLen);
        }
        if (dwRead == (DWORD)-1) {
          if (!dwTransferred) dwTransferred = (DWORD)-1;
          break;
        }
        if (dwTimeout != INFINITE) {
          dwTimeout = (dwTime < dwTimeout) ? (dwTimeout - dwTime) : 0;
        }
        dwLen = dwLen - dwRead;
        pData += dwRead;
        dwTransferred += dwRead;
      } while (dwLen && dwTimeout);
    }
  }

  DEBUGMSG(ZONE_PORT_READ, (TEXT("HcUsb: -CVirtualPort::ReadData(this = %p, ret = %d)\r\n"), this, dwTransferred));

  return dwTransferred;
}

//////////////////////////////////////////////////////////////////////////////
//
// Write data to the virtual USB port.
//
// Parameters:
// pData: The buffer containing the data. In case of an ndis port there is a
//        pointer to a "HCCMD_NDIS_DATA" structure containing the buffer
//        passed. The virtual port is handling these pointers in it's internal
//        ring buffer. If the write has finished the hFinishEvent included in
//        the structure will be set. For handling of these ndis buffers the
//        application is responsible.
// dwLen: The number of bytes to write. In case of a ndis port there is only
//        a pointer given, so the size should be 4.
//
// Return:
// The number of actually written bytes, -1 indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
DWORD CVirtualPort::WriteData(PBYTE pData, DWORD dwLen) {
DWORD   dwWritten;
DWORD   dwTransferred;
DWORD   dwTime;
DWORD   dwWaitTime;
DWORD   dwTimeout;

  DEBUGMSG(ZONE_PORT_WRITE, (TEXT("HcUsb: +CVirtualPort::WriteData(%p, %d)\r\n"), pData, dwLen));

  SYNCBLOCK(m_csLock);

  if (!IsPortOpen()) {
    DEBUGMSG(ZONE_PORT_WRITE | ZONE_ERROR, (TEXT("HcUsb: -CVirtualPort::WriteData(-1) - Port not open\r\n")));
    return (DWORD)-1;
  }

  // Calculate the timeout to be used
  dwTimeout = 0;
  if ((m_Timeouts.WriteTotalTimeoutMultiplier == MAXDWORD) || (m_Timeouts.WriteTotalTimeoutConstant == MAXDWORD)) {
    dwTimeout = INFINITE;
  } else {
    dwTimeout = (m_Timeouts.WriteTotalTimeoutMultiplier * dwLen) + m_Timeouts.WriteTotalTimeoutConstant;
    if (!dwTimeout) {
      dwTimeout = INFINITE;
    }
  }

  UNSYNCBLOCK(m_csLock);

  dwTransferred = 0;
  dwWaitTime = dwTimeout;
  dwTime = GetTickCount();
  while (dwLen && dwWaitTime) {
    if (m_DevType == HC_CMD) {
      dwWritten = m_pDefaultPipe->TransferVendorData(TRUE, m_bUsbIfaceNumber, REQ_SEND_ENCAPSULATED_CMD, 0, pData, (USHORT)dwLen, dwWaitTime);
      if (dwWritten != (DWORD)-1) {
        DEBUGMSG(ZONE_PORT_WRITE | ZONE_PORT_DATA, (TEXT("HcUsb:  CVirtualPort::WriteData() - Written %d bytes on cmd port\r\n"), dwWritten));
        DEBUGHEX(ZONE_PORT_DATA, TEXT("HcUsb:"), pData, dwWritten);
        pData += dwWritten;
        dwLen -= dwWritten;
        dwTransferred += dwWritten;
        if (dwLen) {
          DEBUGMSG(ZONE_PORT_WRITE, (TEXT("HcUsb:  CVirtualPort::WriteData() - failed to write all data at once on CMD port\r\n")));
          if (dwTimeout != INFINITE) {
            dwWaitTime = GetTickCount();
            if ((dwWaitTime - dwTime) < dwTimeout) {
              dwWaitTime = dwTimeout - (dwWaitTime - dwTime);
            } else {
              dwWaitTime = 0;
            }
          }
        }
      } else {
        DEBUGMSG(ZONE_ERROR, (TEXT("HcUsb:  CVirtualPort::WriteData() - Failed: TransferVendorData() on cmd port\r\n")));
        SYNCBLOCK(m_csLock);
        if (!IsPortOpen()) {
          if (!dwTransferred) {
            dwTransferred = -1;
          }
          break;
        }
      }
    } else {
      if (m_DevType == HC_NDIS) {
        DEBUGMSG(ZONE_PORT_DATA, (TEXT("HcUsb:  CVirtualPort::WriteData() - write NDIS data\r\n")));
        dwWritten = m_WriteBuffer.Write((PBYTE)(&pData), sizeof(PHCCMD_NDIS_DATA));
      } else {
        dwWritten = m_WriteBuffer.Write(pData, dwLen);
      }
      if (dwWritten == (DWORD)-1) {
        if (!dwTransferred) dwTransferred = (DWORD)-1;
        break;
      }
      pData += dwWritten;
      dwLen -= dwWritten;
      dwTransferred += dwWritten;
      if (dwLen) {
        dwTime = m_WriteBuffer.WaitForSpace(dwWaitTime);
        if (dwTime == (DWORD)-1) {
          if (!dwTransferred) {
            dwTransferred = (DWORD)-1;
          }
          break;
        }
        if (dwWaitTime != INFINITE) {
          dwWaitTime = dwWaitTime - dwTime;
        }
      }
    }
  }

  DEBUGMSG(ZONE_PORT_WRITE, (TEXT("HcUsb: -CVirtualPort::WriteData(%d)\r\n"), dwTransferred));

  return dwTransferred;
}

//////////////////////////////////////////////////////////////////////////////
//
// Private implementation
//
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//
// Check if our virtual port is open and valid, set error value if not.
//
// Parameters:
// None.
//
// Return:
// TRUE indicates that port is open. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CVirtualPort::IsPortOpen(void) {
  if (!m_dwOpenCount || !m_pUsbHandling->IsUSBAttached() || (WaitForSingleObject(m_hCloseEvent, 0) == WAIT_OBJECT_0)) {
    // Release all pending WaitCommEvent() calls
    //m_WaitCommEvent.ReleaseWaitEvents(0);
    m_WaitCommEvent.RemoveInstance(0);
    SetLastError(ERROR_INVALID_HANDLE);
    // Avoid spinning application threads
    Sleep(10);
    return FALSE;
  }
  return TRUE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Reset the existing pipes.
//
// Parameters:
// None.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CVirtualPort::ResetPipes(void) {
BOOL fRet = TRUE;

  if (m_DevType != HC_CMD) {
    if (!m_BulkOutPipe.Reset()) fRet = FALSE;
    if (!m_BulkInPipe.Reset()) fRet = FALSE;
  } else {
    if (!m_pDefaultPipe->Reset()) fRet = FALSE;
  }
  if (m_IrqInPipe.IsValid()) {
    if (!m_IrqInPipe.Reset()) fRet = FALSE;
  }

  return fRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Abort any pending Read(), Write() or WaitOnMask() calls.
//
// Parameters:
// None.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CVirtualPort::AbortPendingCalls(void) {
BOOL fRet = TRUE;

  if (!m_ReadBuffer.AbortWaitForData()) fRet = FALSE;
  if (!m_ReadBuffer.AbortWaitForSpace()) fRet = FALSE;
  if (!m_WriteBuffer.AbortWaitForData()) fRet = FALSE;
  if (!m_WriteBuffer.AbortWaitForSpace()) fRet = FALSE;
  if (!m_WaitCommEvent.ReleaseWaitEvents(0)) fRet = FALSE;
  return fRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Thread reading data from the in pipe and copying it into our buffer.
//
// Parameters:
// lpParameter: Pointer to the corresponding class instance.
//
// Return:
// 0 indicates success, any other value indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
DWORD WINAPI CVirtualPort::ThreadReadData(LPVOID lpParameter) {
CVirtualPort*      pInstance = (CVirtualPort*)lpParameter;
BYTE               UsbReadBuffer[USB_TRANSFER_SIZE];
PHCCMD_NDIS_DATA   pNdisData;
BYTE*              pBuf;
DWORD              dwRead;
DWORD              dwWritten;

  DEBUGMSG(ZONE_PORT | ZONE_PORT_READ, (TEXT("HcUsb: +CVirtualPort::ThreadReadData(%p)\r\n"), pInstance));

  while (WaitForSingleObject(pInstance->m_hCloseEvent, 0) == WAIT_TIMEOUT) {
    if (pInstance->m_DevType == HC_NDIS) {
      if (pInstance->m_ReadBuffer.WaitForData(INFINITE) != (DWORD)-1) {
        dwRead = pInstance->m_ReadBuffer.Read((PBYTE)(&pNdisData), sizeof(PHCCMD_NDIS_DATA));
        if (dwRead == sizeof(PHCCMD_NDIS_DATA)) {
          while (TRUE) {
            DEBUGMSG(ZONE_PORT_READ, (TEXT("HcUsb:  CVirtualPort::ThreadReadData() - Starting wait for ndis data\r\n")));
            dwRead = pInstance->m_BulkInPipe.TransferData(pNdisData->pData, pNdisData->dwLen, INFINITE);
            if (!dwRead || (dwRead == (DWORD)-1)) {
              DWORD dwError = GetLastError();
              DEBUGMSG(ZONE_PORT_READ, (TEXT("HcUsb:  CVirtualPort::ThreadReadData() - %s\r\n"), !dwRead ? TEXT("No data read") : TEXT("Error during USB transfer")));
              if (!pInstance->IsPortOpen()) {
                DEBUGMSG(ZONE_PORT_READ | ZONE_ERROR, (TEXT("HcUsb: -CVirtualPort::ThreadReadData(-1) - Port has been invalidated\r\n")));
                pNdisData->dwTransferred = (DWORD)-1;
                SetEvent(pNdisData->hFinishEvent);
                pInstance->AbortPendingCalls();
                return (DWORD)-1;
              }
              if (dwError == ERROR_CAN_NOT_COMPLETE) {
                DEBUGMSG(ZONE_PORT_READ | ZONE_ERROR, (TEXT("HcUsb: -CVirtualPort::ThreadReadData(-1) - access error\r\n")));
                pNdisData->dwTransferred = (DWORD)-1;
                SetEvent(pNdisData->hFinishEvent);
                pInstance->AbortPendingCalls();
                return (DWORD)-1;
              }
            } else {
              DEBUGMSG(ZONE_PORT_READ | ZONE_PORT_DATA, (TEXT("HcUsb:  CVirtualPort::ThreadReadData() - Read %d bytes on ndis port\r\n"), dwRead));
              DEBUGHEX(ZONE_PORT_DATA, TEXT("HcUsb:"), UsbReadBuffer, dwRead);
              pNdisData->dwTransferred = dwRead;
              SetEvent(pNdisData->hFinishEvent);
              break;
            }
          }
        }
      }
    } else {
      DEBUGMSG(ZONE_PORT_READ, (TEXT("HcUsb:  CVirtualPort::ThreadReadData() - Starting wait for data\r\n")));
      dwRead = pInstance->m_BulkInPipe.TransferData(UsbReadBuffer, USB_TRANSFER_SIZE, INFINITE);
      if (!dwRead || (dwRead == (DWORD)-1)) {
        DWORD dwError = GetLastError();
        DEBUGMSG(ZONE_PORT_READ, (TEXT("HcUsb:  CVirtualPort::ThreadReadData() - %s\r\n"), !dwRead ? TEXT("No data read") : TEXT("Error during USB transfer")));
        if (!pInstance->IsPortOpen()) {
          DEBUGMSG(ZONE_PORT_READ | ZONE_ERROR, (TEXT("HcUsb: -CVirtualPort::ThreadReadData(-1) - Port has been invalidated\r\n")));
          pInstance->AbortPendingCalls();
          return (DWORD)-1;
        }
        if (dwError == ERROR_CAN_NOT_COMPLETE) {
          DEBUGMSG(ZONE_PORT_READ | ZONE_ERROR, (TEXT("HcUsb: -CVirtualPort::ThreadReadData(-1) - access error\r\n")));
          pInstance->AbortPendingCalls();
          return (DWORD)-1;
        }

        // Insert a delay according to given error condition
        DWORD dwDelay = 500;
        if (dwRead == 0) dwDelay = 0;
        if (dwError == ERROR_ACCESS_DENIED) dwDelay = 1000;
        if (WaitForSingleObject(pInstance->m_hCloseEvent, dwDelay) == WAIT_OBJECT_0) {
          DEBUGMSG(ZONE_PORT_READ, (TEXT("HcUsb:  CVirtualPort::ThreadReadData() - Close event detected\r\n")));
          return -1;
        }
      } else {
        DEBUGMSG(ZONE_PORT_READ | ZONE_PORT_DATA, (TEXT("HcUsb:  CVirtualPort::ThreadReadData(this=%p) - Read %d bytes\r\n"), pInstance, dwRead));
        DEBUGHEX(ZONE_PORT_DATA, TEXT("HcUsb:"), UsbReadBuffer, dwRead);
        pBuf = UsbReadBuffer;
        if (!pInstance->m_ReadBuffer.GetSpace()) {
          pInstance->m_WaitCommEvent.SetWaitEvent(EV_RXCHAR);
        }
        while (dwRead && (pInstance->m_ReadBuffer.WaitForSpace(INFINITE) != (DWORD)-1)) {
          dwWritten = pInstance->m_ReadBuffer.Write(pBuf, dwRead);
          pBuf += dwWritten;
          dwRead -= dwWritten;
          if (dwRead) {
            pInstance->m_WaitCommEvent.SetWaitEvent(EV_RXCHAR);
          }
        }
        pInstance->m_WaitCommEvent.SetWaitEvent(EV_RXCHAR);
      }
    }
  }

  DEBUGMSG(ZONE_PORT | ZONE_PORT_READ, (TEXT("HcUsb: -CVirtualPort::ThreadReadData(0)\r\n")));

  return 0;
}

//////////////////////////////////////////////////////////////////////////////
//
// Thread writing data from the ring buffer to the out pipe.
//
// Parameters:
// lpParameter: Pointer to the corresponding class instance.
//
// Return:
// 0 indicates success, any other value indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
DWORD WINAPI CVirtualPort::ThreadWriteData(LPVOID lpParameter) {
CVirtualPort*      pInstance = (CVirtualPort*)lpParameter;
BYTE               UsbWriteBuffer[USB_TRANSFER_SIZE];
PHCCMD_NDIS_DATA   pNdisData;
BYTE*              pBuf;
DWORD              dwWritten;
DWORD              dwToWrite;

  DEBUGMSG(ZONE_PORT_WRITE, (TEXT("HcUsb: +CVirtualPort::ThreadWriteData(%p)\r\n"), pInstance));

  while (WaitForSingleObject(pInstance->m_hCloseEvent, 0) == WAIT_TIMEOUT) {
    if (pInstance->m_WriteBuffer.WaitForData(INFINITE) != (DWORD)-1) {
      DEBUGMSG(ZONE_PORT_WRITE, (TEXT("HcUsb:  CVirtualPort::ThreadWriteData() - Data in ring buffer detected\r\n")));
      if (pInstance->m_DevType == HC_NDIS) {
        // With ndis we have pointers to the packet data in our ring buffer
        dwToWrite = pInstance->m_WriteBuffer.Read((PBYTE)(&pNdisData), sizeof(PHCCMD_NDIS_DATA));
        pBuf = pNdisData->pData;
        dwToWrite = pNdisData->dwLen;
      } else {
        dwToWrite = pInstance->m_WriteBuffer.Read(UsbWriteBuffer, USB_TRANSFER_SIZE);
        pBuf = UsbWriteBuffer;
      }
      while (dwToWrite) {
        dwWritten = pInstance->m_BulkOutPipe.TransferData(pBuf, dwToWrite, INFINITE, pInstance->m_WriteBuffer.GetLevel() ? FALSE : TRUE);
        if (!dwWritten || (dwWritten == (DWORD)-1)) {
          DWORD dwError = GetLastError();
          DEBUGMSG(ZONE_PORT_WRITE | ZONE_ERROR, (TEXT("HcUsb:  CVirtualPort::ThreadWriteData() - %s\r\n"), !dwWritten ? TEXT("No data written") : TEXT("Error during USB transfer")));
          if (!pInstance->IsPortOpen()) {
            DEBUGMSG(ZONE_PORT_WRITE | ZONE_ERROR, (TEXT("HcUsb: -CVirtualPort::ThreadWriteData(-1) - Port has been invalidated\r\n")));
            if (pInstance->m_DevType == HC_NDIS) {
              pNdisData->dwTransferred = (DWORD)-1;
              SetEvent(pNdisData->hFinishEvent);
            }
            pInstance->AbortPendingCalls();
            return (DWORD)-1;
          }
          if (dwError == ERROR_CAN_NOT_COMPLETE) {
            DEBUGMSG(ZONE_PORT_WRITE | ZONE_ERROR, (TEXT("HcUsb: -CVirtualPort::ThreadWriteData(-1) - access error\r\n")));
            pInstance->AbortPendingCalls();
            return (DWORD)-1;
          }
          Sleep(500);
        } else {
          DEBUGMSG(ZONE_PORT_WRITE | ZONE_PORT_DATA, (TEXT("HcUsb:  CVirtualPort::ThreadWriteData() - Written %d bytes\r\n"), dwWritten));
          DEBUGHEX(ZONE_PORT_DATA, TEXT("HcUsb:"), pBuf, dwWritten);
          pBuf += dwWritten;
          dwToWrite -= dwWritten;
        }
        if (pInstance->m_DevType == HC_NDIS) {
          // With ndis we're lost if the packet wasn't transferred at once, so we finish in any case
          dwToWrite = 0;
          pNdisData->dwTransferred = dwWritten;
          SetEvent(pNdisData->hFinishEvent);
        }
      }
      if (!pInstance->m_WriteBuffer.GetLevel() && (pInstance->m_DevType != HC_NDIS)) {
        pInstance->m_WaitCommEvent.SetWaitEvent(EV_TXEMPTY);
      }
    }
  }

  DEBUGMSG(ZONE_PORT_WRITE, (TEXT("HcUsb: -CVirtualPort::ThreadWriteData(0)\r\n")));

  return 0;
}

//////////////////////////////////////////////////////////////////////////////
//
// Thread started to read data on the interrupt in pipe e.g. to detect
// changes of the virtual modem control signals).
//
// Parameters:
// lpParameter: Pointer to the corresponding class instance.
//
// Return:
// 0 indicates success, any other value indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
DWORD WINAPI CVirtualPort::ThreadReadIrqData(LPVOID lpParameter) {
CVirtualPort*  pInstance = (CVirtualPort*)lpParameter;
BYTE           Buf[USB_TRANSFER_SIZE];
DWORD          dwRead;

  DEBUGMSG(ZONE_PORT_READ, (TEXT("HcUsb: +CVirtualPort::ThreadReadIrqData(%p)\r\n"), pInstance));

  // Loop until port instance is closed
  while (WaitForSingleObject(pInstance->m_hCloseEvent, 0) != WAIT_OBJECT_0) {
    dwRead = pInstance->m_IrqInPipe.TransferData(Buf, USB_TRANSFER_SIZE, INFINITE);
    DWORD dwError = GetLastError();
    if (!dwRead || (dwRead == (DWORD)-1)) {
      DEBUGMSG(ZONE_PORT_READ, (TEXT("HcUsb:  CVirtualPort::ThreadReadIrqData() - %s\r\n"), !dwRead ? TEXT("No data read") : TEXT("Error during USB transfer")));
      if (!pInstance->IsPortOpen()) {
        DEBUGMSG(ZONE_PORT_READ | ZONE_ERROR, (TEXT("HcUsb: -CVirtualPort::ThreadReadIrqData(-1) - Port has been invalidated\r\n")));
        pInstance->AbortPendingCalls();
        return (DWORD)-1;
      }
      if ( dwError == ERROR_CAN_NOT_COMPLETE ) {
        DEBUGMSG(ZONE_PORT_READ | ZONE_ERROR, (TEXT("HcUsb: -CVirtualPort::ThreadReadIrqData(-1) - access error\r\n")));
        pInstance->AbortPendingCalls();
        return (DWORD)-1;
      }

      DWORD to = 500;
      if ( dwError == ERROR_ACCESS_DENIED ) {
        to = 1000;
      }

      if (WAIT_OBJECT_0 == WaitForSingleObject(pInstance->m_hCloseEvent, to)) {
      DEBUGMSG(ZONE_PORT_READ, (TEXT("HcUsb:  CVirtualPort::ThreadReadIrqData() - Close event detected\r\n")));
      break;
      }
    } else {
      DEBUGMSG(ZONE_PORT_DATA, (TEXT("HcUsb:  CVirtualPort::ThreadReadIrqData() - Read %d bytes\r\n"), dwRead));
      DEBUGHEX(ZONE_PORT_DATA, TEXT("HcUsb:"), Buf, dwRead);

      DWORD  dwTodo = dwRead;
      DWORD  dwDone = 0, dwHandled = 0;
      BYTE*  pBuf = Buf;

      while ( dwTodo >= sizeof(USB_DEVICE_REQUEST) ) {
        PUSB_DEVICE_REQUEST pDevRequest = (PUSB_DEVICE_REQUEST)(pBuf + dwDone);
        DWORD dwLen = pDevRequest->wLength;
        if ( pInstance->m_DevType == HC_CMD ) {
          dwHandled = pInstance->HandleDeviceRequest(pDevRequest);
        } else {
          dwHandled = pInstance->GetUsbHandling()->DispatchDeviceRequest(pDevRequest);
        }

        if (dwHandled == (DWORD)-1) {
          DEBUGMSG(ZONE_PORT_DATA, (TEXT("HcUsb: CVirtualPort::ThreadReadIrqData() - error dispatching request\r\n")));
          // skip this request with it's data and continue handling data from IRQ pipe
          dwHandled = pDevRequest->wLength + sizeof(USB_DEVICE_REQUEST);
          break;
        }

        dwTodo -= dwHandled;
        dwDone += dwHandled;
        if (( dwTodo != 0) && (dwTodo < sizeof(USB_DEVICE_REQUEST))) {
          DEBUGMSG(ZONE_PORT_DATA, (TEXT("HcUsb:  CVirtualPort::ThreadReadIrqData() - incomplete/invalid request (%d byte remain)\r\n"), dwTodo));
          DEBUGHEX(ZONE_PORT_SIGNALS | ZONE_ERROR, TEXT("HcUsb:"), pBuf +dwDone, dwTodo);
          // TODO: move remaining to begin of buffer and continue reading ...
          break;
        }
      }
    }
  }

  DEBUGMSG(ZONE_PORT_READ, (TEXT("HcUsb: -CVirtualPort::ThreadReadIrqData(0)\r\n")));

  return 0;
}

//////////////////////////////////////////////////////////////////////////////
//
//////////////////////////////////////////////////////////////////////////////
DWORD CVirtualPort::HandleDeviceRequest( PUSB_DEVICE_REQUEST pDevRequest ) {
  DEBUGMSG(ZONE_PORT_SIGNALS, (TEXT("HcUsb:  +CVirtualPort::HandleDeviceRequest() => %p\r\n"), this));
  if (IsSerial()) {
    DEBUGMSG(ZONE_PORT_SIGNALS, (TEXT("HcUsb:   CVirtualPort::HandleDeviceRequest() on serial port\r\n")));

    BYTE    bExpectedRequestType = USB_REQUEST_DEVICE_TO_HOST | USB_REQUEST_CLASS | USB_REQUEST_FOR_INTERFACE;
    DWORD   dwCurrentSize;

    dwCurrentSize = pDevRequest->wLength + sizeof(USB_DEVICE_REQUEST);
    if ((pDevRequest->wIndex == m_bUsbIfaceNumber) && (pDevRequest->bmRequestType == bExpectedRequestType)) {
      if (pDevRequest->bRequest == NTF_SERIAL_STATE) {
        if (dwCurrentSize == (sizeof(USB_DEVICE_REQUEST) + 2)) {
          // We received a complete SERIAL_STATE frame
          WORD   wSerialState = *((PWORD)((BYTE*)pDevRequest + sizeof(USB_DEVICE_REQUEST)));
          DWORD  dwChangedMask;
          BOOL   fRing, fDsr, fCd;

          // There is no virtual CTS signal
          fRing  = (wSerialState & LINE_STATE_RING_MASK) ? TRUE : FALSE;
          fDsr   = (wSerialState & LINE_STATE_DSR_MASK) ? TRUE : FALSE;
          fCd    = (wSerialState & LINE_STATE_CD_MASK) ? TRUE : FALSE;
          DEBUGMSG(ZONE_PORT_SIGNALS, (TEXT("HcUsb:  CVirtualPort::HandleDeviceRequest on Serial Port - Received Serial State: Ring(%d), Dsr(%d), Cd(%d)\r\n"), fRing, fDsr, fCd));

          SYNCBLOCK(m_csLock);
          dwChangedMask = 0;
          SetEvent( m_hLineStateValidEvent );
          if (fRing != m_fRing) {
            m_fRing = fRing;
            dwChangedMask |= EV_RING;
          }
          if (fDsr != m_fDsr) {
            m_fDsr = fDsr;
            dwChangedMask |= EV_DSR;
          }
          if (fCd != m_fCd) {
            m_fCd = fCd;
            dwChangedMask |= EV_RLSD;
          }
          if (dwChangedMask) {
            m_WaitCommEvent.SetWaitEvent(dwChangedMask);
          }
        }
        else {
          DEBUGMSG(ZONE_PORT_SIGNALS | ZONE_ERROR, (TEXT("HcUsb:  CVirtualPort::HandleDeviceRequest() on Serial Port - Received wrong SERIAL_STATE frame\r\n")));
        }
      } else if (pDevRequest->bRequest == NTF_CONNECTION_SPEED_CHG) {
        if (dwCurrentSize == (sizeof(USB_DEVICE_REQUEST) + 8)) {
          // We received a complete CONNECTION_SPEED_CHANGE frame
          DWORD  dwUpRate    = *((PDWORD)(pDevRequest + sizeof(USB_DEVICE_REQUEST)));
          DWORD  dwDownRate  = *((PDWORD)(pDevRequest + sizeof(USB_DEVICE_REQUEST) + sizeof(DWORD)));
          DEBUGMSG(ZONE_PORT_SIGNALS, (TEXT("HcUsb:  CVirtualPort::HandleDeviceRequest on Serial Port - Received Connection Speed: Up-Rate: %d Baud, Down-Rate: %d Baud\r\n"), dwUpRate, dwDownRate));
        }
        else {
          DEBUGMSG(ZONE_PORT_SIGNALS | ZONE_ERROR, (TEXT("HcUsb:  CVirtualPort::HandleDeviceRequest() on Serial Port - Received wrong CONNECTION_SPEED_CHG frame\r\n")));
        }
      } else if (pDevRequest->bRequest == NTF_NETWORK_CONNECTION) {
          // ignore this notification
      } else {
        DEBUGMSG(ZONE_PORT_SIGNALS | ZONE_ERROR, (TEXT("HcUsb:  CVirtualPort::HandleDeviceRequest() on Serial Port - Received unknown notification\r\n")));
        DEBUGHEX(ZONE_PORT_SIGNALS | ZONE_ERROR, TEXT("HcUsb:"), (BYTE*)pDevRequest, dwCurrentSize);
      }
    } else {
      DEBUGMSG(ZONE_PORT_SIGNALS | ZONE_ERROR, (TEXT("HcUsb:  CVirtualPort::HandleDeviceRequest() on Serial Port - Received wrong Index (expected: %d, received: %d) or Request type (expected: 0x%02x, received: 0x%02x)\r\n"), m_bUsbIfaceNumber, pDevRequest->wIndex, bExpectedRequestType, pDevRequest->bmRequestType));
      DEBUGHEX(ZONE_PORT_SIGNALS | ZONE_ERROR, TEXT("HcUsb:"), (BYTE*)pDevRequest, dwCurrentSize);
    }
    return dwCurrentSize;
  } else if (m_DevType == HC_CMD) {
    DEBUGMSG(ZONE_PORT_SIGNALS, (TEXT("HcUsb:   CVirtualPort::HandleDeviceRequest() on NDIS cmd port\r\n")));
    if (pDevRequest->bmRequestType == (USB_REQUEST_DEVICE_TO_HOST | USB_REQUEST_CLASS | USB_REQUEST_FOR_INTERFACE)) {
      if ((pDevRequest->bRequest == NTF_RESPONSE_AVAILABLE) && (pDevRequest->wLength == 0)) {
        if (m_bUsbIfaceNumber == pDevRequest->wIndex) {
          // We received a notification that there is command data available on the default pipe -> unlock read thread
          SetEvent(m_hCmdDataAvailableEvent);
          DEBUGMSG(ZONE_PORT_SIGNALS, (TEXT("HcUsb:  CVirtualPort::HandleDeviceRequest() on CMD Port - Received NTF_RESPONSE_AVAILABLE for Ndis interface\r\n")));
        } else {
          DEBUGMSG(ZONE_PORT_READ | ZONE_ERROR, (TEXT("HcUsb:  CVirtualPort::HandleDeviceRequest on CMD Port - Received GetEncapsulatedCmd for wrong interface %d\r\n"), pDevRequest->wIndex));
        }
        return sizeof(USB_DEVICE_REQUEST);
      } else if ((pDevRequest->bRequest == NTF_CONNECTION_SPEED_CHG) && (pDevRequest->wLength == 8)) {
        if (m_bUsbIfaceNumber == pDevRequest->wIndex) {
          // We received a complete CONNECTION_SPEED_CHANGE frame
          DWORD  dwUpRate    = *((PDWORD)(pDevRequest + sizeof(USB_DEVICE_REQUEST)));
          DWORD  dwDownRate  = *((PDWORD)(pDevRequest + sizeof(USB_DEVICE_REQUEST) + sizeof(DWORD)));
          DEBUGMSG(ZONE_PORT_READ, (TEXT("HcUsb:  CVirtualPort::HandleDeviceRequest() on CMD Port - Received NTF_CONNECTION_SPEED_CHG - Up-Rate: %d Baud, Down-Rate: %d Baud\r\n"), dwUpRate, dwDownRate));
        } else {
          DEBUGMSG(ZONE_PORT_READ | ZONE_ERROR, (TEXT("HcUsb:  CVirtualPort::HandleDeviceRequest on CMD Port - Received GetEncapsulatedCmd for wrong interface %d\r\n"), pDevRequest->wIndex));
          DEBUGHEX(ZONE_PORT_READ | ZONE_ERROR, TEXT("HcUsb:"), (BYTE*)pDevRequest, sizeof(USB_DEVICE_REQUEST));
        }
        return sizeof(USB_DEVICE_REQUEST) + pDevRequest->wLength;
      } else {
        DEBUGMSG(ZONE_PORT_READ | ZONE_ERROR, (TEXT("HcUsb:  CVirtualPort::HandleDeviceRequest() on Cmd Port - unexpected bmRequestType\r\n"), pDevRequest->wLength ));
        DEBUGHEX(ZONE_PORT_READ | ZONE_ERROR, TEXT("HcUsb:"), (BYTE*)pDevRequest, sizeof(USB_DEVICE_REQUEST) + pDevRequest->wLength );
      }
    } else {
      DEBUGMSG(ZONE_PORT_READ | ZONE_ERROR, (TEXT("HcUsb:  CVirtualPort::HandleDeviceRequest() on Cmd Port - Received wrong USB_DEVICE_REQUEST frame (len=%d)\r\n"), pDevRequest->wLength ));
      DEBUGHEX(ZONE_PORT_READ | ZONE_ERROR, TEXT("HcUsb:"), (BYTE*)pDevRequest, sizeof(USB_DEVICE_REQUEST) + pDevRequest->wLength );
    }
  } else {
    DEBUGMSG(ZONE_PORT_READ | ZONE_ERROR, (TEXT("HcUsb: -CVirtualPort::HandleDeviceRequest(-1) - Unsupported device type: %d\r\n"), m_DevType));
  }

  return -1;
}
