/*****************************************************************************
 * Copyright (C) Cinterion Wireless Modules GmbH
 *****************************************************************************
 * Project: Quinn-CE-USB-Driver
 *****************************************************************************
 * Author : Siemens AG Berlin COM WM RD SW
 *****************************************************************************
 * $Workfile: E:/cc/thinze_ms-d_snap/z_ms-d/src/working/Quinn/HcUsbDrv/UsbPipe.cpp $
 * $Revision: /main/ms-d/42 $
 * $Date: 2013-01-10 15:10:25 $
 * $Author: thinze $
 *****************************************************************************
 * Target:
 *  Microsoft Windows CE 5.0
 *****************************************************************************
 * Description:
 *  Quinn-USB-Driver: Implementation of the class CUsbPipe for USB
 *  pipe access
 *****************************************************************************
 *
 * $Log: E:/cc/thinze_ms-d_snap/z_ms-d/src/working/Quinn/HcUsbDrv/UsbPipe.cpp$ 
 * 
 * /main/ms-d/42   2013-01-10 15:10:25   thinze 
 * added new debug messages to trace host function calls 
 * 
 * /main/ms-d/41   2011-03-02 10:50:42   ctna0450 
 * Avoid sending of unnecessary zero length packages
 *
 * /main/ms-d/40   2011-02-14 14:15:18   ctna0152
 * enhanced error handling
 *
 * /main/ms-d/39   2011-02-02 10:27:55   ctna0450
 * Removed unused USB-Reset workaround for Uranus USB problems
 *
 * /main/ms-d/38   2011-01-18 16:41:29   ctna0152
 * changed debug messages to support analysis of IPIS100069361 & IPIS100069553
 *
 * /main/ms-d/37   2011-01-13 15:26:55   ctna0152
 * added support for zero length packets
 *
 * /main/ms-d/36   2010-12-14 15:14:42   ctna0152
 * changed debug messgaes
 *
 * /main/ms-d/35   2010-12-13 14:22:41   ctna0152
 * added USB reset handling
 *
 * /main/ms-d/34   2009-02-18 11:49:40   ctna0256
 * Set copyright text
 *
 * /main/ms-d/33   2008-08-20 11:39:35   ctna0450
 * Prepared for new disclaimer handling
 *
 * /main/ms-d/32   2008-06-19 14:23:19   ht37010
 * added workaround for USB host function failure
 *
 * /main/ms-d/31   2008-04-11 19:10:07   ht37010
 * - call ResetPipe(TRUE) in TransferData only on USB_STALL_ERROR
 * - added debug msgs for ZONE_USB_HOSTFN_ERROR
 *
 * /main/ms-d/30   2008-04-10 15:11:08   ht37010
 * bugfix: deadlock in UsbPipe.WaitForResume() vs. VPort.Close()
 *
 * /main/ms-d/29   2008-03-03 12:39:12   ht37010
 * added force flag in AbortTransfer() to abort wait-for-resume on close
 *
 * /main/ms-d/28   2008-02-18 15:12:01   ht37010
 * added new event as bugfix for possible deadlock on closing a virtual port (may happen if pipe is waiting for device resume and the port is closed)
 *
 * /main/ms-d/27   2007-12-18 14:06:55   ht37010
 * changed debug messages
 *
 * /main/ms-d/26   2007-12-18 11:42:49   ht37010
 * added USB watchdog for lpResetPipe
 *
 * /main/ms-d/25   2007-08-28 12:51:05   ht37010
 * added debug messages
 *
 * /main/ms-d/24   2007-08-14 12:44:00   ht37010
 * added debug messages
 * return if reset fails in TransferData()
 *
 * /main/ms-d/23   2007-08-02 15:45:30   ht37010
 * added debug messages for host functions
 *
 * /main/ms-d/22   2007-07-17 10:15:28   ht37010
 * corrected encapsulation of reset-after-resume-workaround
 *
 * /main/ms-d/21   2007-05-30 12:58:36   ht37010
 * changed debug messages
 *
 * /main/ms-d/20   2007-04-16 12:23:34   ht37010
 * workaround - reset pipe on resume
 *
 * /main/ms-d/19   2007-03-30 11:20:54   ht37010
 * fix handle error codes on failed read
 *
 * /main/ms-d/18   2007-03-29 16:52:29   ht37010
 * workaround to prevent start of modules mass storage device
 * fix handle error codes on failed read transfer
 *
 * /main/ms-d/17   2007-02-23 10:51:42   ht37010
 * bugfix IPIS100032053
 *
 * /main/ms-d/16   2007-02-16 16:37:51   wm36264
 * Adapted selective suspend/resume notification to MS confirmend interface.
 * Untested and still deactivated by default!
 *
 * /main/ms-d/15   2007-02-02 10:24:20   wm36264
 * Fixed deadlock in case of reset after transfer errors
 *
 * /main/ms-d/14   2006-12-18 16:45:44   wm36264
 * Create ndis port at last in case we haven'r enough pipes
 *
 * /main/ms-d/13   2006-12-14 12:15:52   wm36264
 * Prepared resume notification
 *
 * /main/ms-d/12   2006-12-06 10:37:57   wm36264
 * Activated selective suspend
 *
 * /main/ms-d/11   2006-11-03 17:07:00   wm36264
 * Continued to prepare selective suspend handling
 *
 * /main/ms-d/10   2006-10-20 12:16:22   wm36264
 * The ndis irq pipe notifies us when there is qmi data available on the default pipe
 *
 * /main/ms-d/9   2006-10-18 12:34:03   wm36264
 * Several modifications regarding default pipe handling for qmi
 *
 * /main/ms-d/8   2006-10-13 17:10:32   wm36264
 * Fixed some debug traces
 *
 * /main/ms-d/7   2006-10-12 16:52:33   wm36264
 * For a possible future selective suspend implementation it is much easier when there is only one pipe class
 *
 * /main/ms-d/6   2006-10-10 15:14:30   wm36264
 * Prepared usage of ndis interface number
 *
 * /main/ms-d/5   2006-10-06 18:00:22   wm36264
 * Make virtual ports fully instancable and use different device names for serial, ndis and qmi devices
 *
 * /main/ms-d/4   2006-09-29 15:33:12   wm36264
 * Consider latest findings about QMI and NDIS handling
 *
 * /main/ms-d/3   2006-09-22 14:33:36   wm36264
 * Cosmetics mostly
 *
 * /main/ms-d/2   2006-09-21 17:05:57   wm36264
 * Prepared access to QMI interface
 *
 * /main/ms-d/1   2006-09-18 15:31:52   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
//
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//
// UsbPipe.cpp
//
// This file contains the implementation of the class "CUsbPipe" which
// implements the handling of an USB pipe and "CUsbDefaultPipe" handling acces
// to the devices control pipes.
//
//////////////////////////////////////////////////////////////////////////////

#include "precomp.h"

//////////////////////////////////////////////////////////////////////////////
//
// CUsbPipe - Public interface
//
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//
// Constructor
//
// Parameters:
// None.
//
//////////////////////////////////////////////////////////////////////////////
CUsbPipe::CUsbPipe() {
  InitializeCriticalSection(&m_csLock);
  m_hCompletionEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  m_hDefaultCompletionEvent = NULL;
  m_hAbortTransferEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  m_pFuncs = NULL;
  m_hPipe = NULL;
  m_hDevice = NULL;
  m_hTransfer = NULL;
  m_bType = (BYTE)-1;
  m_bIndex = (BYTE)-1;
  m_fIsDefaultPipe = FALSE;
  m_pSelectiveSuspend = NULL;

  m_wMaxPacketSize = 0;

#ifdef USE_WATCHDOG_RESET_PIPE
  m_pWDResetPipe = new CUsbWatchDog( 5000 );
#endif

#ifdef USE_WA_RETRY_ON_HOSTFN_FAIL
  HKEY  hKey;
  DWORD dwRegDataSize;

  if (SUCCEEDED(RegOpenKeyEx(HKEY_LOCAL_MACHINE, HCUSBDRV_CLIENT_REGKEY, 0, 0, &hKey)) || SUCCEEDED(RegOpenKeyEx(HKEY_LOCAL_MACHINE, HCUSBDRV_CLIENT_REGKEY_COMPATIBLITY, 0, 0, &hKey))) {
    dwRegDataSize = sizeof(DWORD);
    if (FAILED(RegQueryValueEx(hKey, USB_RETRY_DELAY_NAME, 0, NULL, (LPBYTE)(&m_dwHostFnRetryDelay), &dwRegDataSize))) {
      m_dwHostFnRetryDelay = 0;
    }
    RegCloseKey(hKey);
  }

  if (SUCCEEDED(RegOpenKeyEx(HKEY_LOCAL_MACHINE, HCUSBDRV_CLIENT_REGKEY, 0, 0, &hKey)) || SUCCEEDED(RegOpenKeyEx(HKEY_LOCAL_MACHINE, HCUSBDRV_CLIENT_REGKEY_COMPATIBLITY, 0, 0, &hKey))) {
    dwRegDataSize = sizeof(DWORD);
    if (FAILED(RegQueryValueEx(hKey, USB_RETRY_COUNT_NAME, 0, NULL, (LPBYTE)(&m_dwHostFnRetryCount), &dwRegDataSize))) {
      m_dwHostFnRetryCount = 0;
    }
    RegCloseKey(hKey);
  }
#endif
}

//////////////////////////////////////////////////////////////////////////////
//
// Destructor
//
//////////////////////////////////////////////////////////////////////////////
CUsbPipe::~CUsbPipe() {
#ifdef USE_WATCHDOG_RESET_PIPE
  if ( m_pWDResetPipe != NULL )
      delete m_pWDResetPipe;
#endif
  Deinitialize();
  CloseHandle( m_hAbortTransferEvent);
  CloseHandle(m_hCompletionEvent);
  DeleteCriticalSection(&m_csLock);
}

//////////////////////////////////////////////////////////////////////////////
//
// Allocate required USB resources.
//
// Parameters:
// Device           : Handle to our USB device.
// Funcs            : Pointer to a USB device interface function table.
// Endpoint         : USB endpoint corresponding to pipe. A NULL indicates
//                    that we are a default pipe.
// pSelectiveSuspend: Pointer to selective suspend handling.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CUsbPipe::Initialize(USB_HANDLE Device, LPCUSB_FUNCS Funcs, LPCUSB_ENDPOINT Endpoint, CSelectiveSuspend* pSelectiveSuspend) {
  DEBUGMSG(ZONE_USB_PIPE, (TEXT("HcUsb: +CUsbPipe::Initialize(%p, %p, %p)\r\n"), Device, Funcs, Endpoint));

  SYNCBLOCK(m_csLock);

  if (!Device) return FALSE;
  if (!Funcs) return FALSE;
  if (!pSelectiveSuspend) return FALSE;
  if (!m_hCompletionEvent) return FALSE;

  // Check if we aren't already initialized
  if (m_hDevice || m_hPipe) return FALSE;

  m_pFuncs = Funcs;
  m_hDevice = Device;
  m_pSelectiveSuspend = pSelectiveSuspend;

  if (Endpoint) {
#ifdef USE_WA_RETRY_ON_HOSTFN_FAIL
    DWORD i = 0;
    while ( ( m_hPipe == NULL ) && ( i < m_dwHostFnRetryCount +1 ) ) {
#endif
      DEBUGMSG(ZONE_USB_HOSTFN, (TEXT("HcUsb:  +lpOpenPipe() on pipe %d\r\n"), m_bIndex & 0x7F));
      m_hPipe = m_pFuncs->lpOpenPipe(Device, &Endpoint->Descriptor);
      DEBUGMSG(ZONE_USB_HOSTFN, (TEXT("HcUsb:  -lpOpenPipe(hPipe=%p) on pipe %d\r\n"), m_hPipe, m_bIndex & 0x7F));
#ifdef USE_WA_RETRY_ON_HOSTFN_FAIL
      if ( ( m_hPipe == NULL ) && ( m_dwHostFnRetryCount != 0 ) ) {
        DEBUGMSG(ZONE_ERROR, (TEXT("HcUsb: lpOpenPipe() - %d/%d failed, retry in %d ms\r\n"),
                                         i, m_dwHostFnRetryCount +1, m_dwHostFnRetryDelay));
        Sleep( m_dwHostFnRetryDelay );
      }
      i++;
    }
#endif
    if (!m_hPipe) {
      DEBUGMSG(ZONE_USB_HOSTFN_ERROR, (TEXT("HcUsb: lpOpenPipe() - failed\r\n")));
      DEBUGMSG(ZONE_USB_PIPE | ZONE_ERROR, (TEXT("HcUsb: -CUsbPipe::Initialize() error: %d\r\n"), GetLastError()));
      Deinitialize();
      return FALSE;
    }
    m_wMaxPacketSize = Endpoint->Descriptor.wMaxPacketSize;
    m_bType = Endpoint->Descriptor.bmAttributes & USB_ENDPOINT_TYPE_MASK;
    m_bIndex = Endpoint->Descriptor.bEndpointAddress;

    // clear the halt feature on each pipe (prevents booting of modules mass storage device for driver installation)
    DEBUGMSG(ZONE_USB_PIPE, (TEXT("HcUsb: CUsbPipe::Initialize() - clear STALL on %s: %p\r\n"), GET_PIPE_STRING(m_bType, m_bIndex), m_hPipe));
    DEBUGMSG(ZONE_USB_HOSTFN, (TEXT("HcUsb:  +lpClearFeature() on pipe %d\r\n"), m_bIndex & 0x7F));
    USB_TRANSFER hTransfer = m_pFuncs->lpClearFeature(m_hDevice, NULL, NULL, USB_SEND_TO_ENDPOINT, USB_FEATURE_ENDPOINT_STALL, m_bIndex);
    DEBUGMSG(ZONE_USB_HOSTFN, (TEXT("HcUsb:  -lpClearFeature() on pipe %d\r\n"), m_bIndex & 0x7F));
    if (hTransfer) {
      DEBUGMSG(ZONE_USB_HOSTFN, (TEXT("HcUsb:  +lpCloseTransfer() on pipe %d\r\n"), m_bIndex&0x7F));
      if ( m_pFuncs->lpCloseTransfer(hTransfer) == FALSE ) {
          DEBUGMSG(ZONE_USB_HOSTFN_ERROR, (TEXT("HcUsb: lpCloseTransfer() - failed\r\n")));
      }
      DEBUGMSG(ZONE_USB_HOSTFN, (TEXT("HcUsb:  -lpCloseTransfer() on pipe %d\r\n"), m_bIndex&0x7F));
    } else {
      DEBUGMSG(ZONE_USB_HOSTFN_ERROR, (TEXT("HcUsb: lpClearFeature() - failed\r\n")));
      DEBUGMSG(ZONE_USB_PIPE|ZONE_ERROR, (TEXT("HcUsb: CUsbPipe::Initialize() - clear STALL on %s error: %d\r\n"), GET_PIPE_STRING(m_bType, m_bIndex), GetLastError()));
    }
  } else {
    // We have a default pipe
    m_hDefaultCompletionEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
    if (!m_hDefaultCompletionEvent) {
      Deinitialize();
      DEBUGMSG(ZONE_USB_PIPE, (TEXT("HcUsb: -CUsbPipe::Initialize(FALSE) - no default complete\r\n")));
      return FALSE;
    }
    m_fIsDefaultPipe = TRUE;
    m_bType = USB_ENDPOINT_TYPE_CONTROL;
    m_bIndex = 0;
  }

  DEBUGMSG(ZONE_USB_PIPE, (TEXT("HcUsb: -CUsbPipe::Initialize(1) - Opened %s: %p\r\n"), GET_PIPE_STRING(m_bType, m_bIndex), m_hPipe));

  return TRUE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Release allocated USB resources.
//
// Parameters:
// None.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CUsbPipe::Deinitialize(void) {
BOOL fRet = TRUE;

  DEBUGMSG(ZONE_USB_PIPE, (TEXT("HcUsb: +CUsbPipe::Deinitialize() %s Typ=0x%02X Idx=0x%02X\r\n"), GET_PIPE_STRING(m_bType, m_bIndex), m_bType, m_bIndex));

  DEBUGMSG(ZONE_LOCK, (TEXT("HcUsb: CUsbPipe::Deinitialize() - sync: +CUsbPipe id=%d\r\n"), m_bIndex&0x7F));
  SYNCBLOCK(m_csLock);
  DEBUGMSG(ZONE_LOCK, (TEXT("HcUsb: CUsbPipe::Deinitialize() - sync: -CUsbPipe id=%d\r\n"), m_bIndex&0x7F));

  if (m_pFuncs) {
    if (!AbortTransfer()) {
      DEBUGMSG(ZONE_USB_PIPE | ZONE_ERROR, (TEXT("HcUsb: CUsbPipe::Deinitialize() error aborting running transfer: %d\r\n"), GetLastError()));
      fRet = FALSE;
    }

    if (m_hPipe) {
      DEBUGMSG(ZONE_USB_HOSTFN, (TEXT("HcUsb: +lpClosePipe() on pipe %d\r\n"), m_bIndex & 0x7F));
      if (!m_pFuncs->lpClosePipe(m_hPipe)) {
        DWORD le = GetLastError();
        DEBUGMSG(ZONE_USB_HOSTFN_ERROR, (TEXT("HcUsb: lpClosePipe() - failed\r\n")));
        if (le != 0) {
          DEBUGMSG(ZONE_USB_PIPE | ZONE_ERROR, (TEXT("HcUsb: CUsbPipe::Deinitialize() error: %d\r\n"), le));
        }
        DEBUGMSG(ZONE_USB_HOSTFN, (TEXT("HcUsb: -lpClosePipe(FALSE)\r\n")));
        fRet = FALSE;
      } else {
        DEBUGMSG(ZONE_USB_HOSTFN, (TEXT("HcUsb: -lpClosePipe(TRUE)\r\n")));
      }
    }
    DEBUGMSG(ZONE_USB_PIPE, (TEXT("HcUsb: CUsbPipe::Deinitialize() - Closed %s: %p\r\n"), GET_PIPE_STRING(m_bType, m_bIndex), m_hPipe));
  }

  if (m_hDefaultCompletionEvent) CloseHandle(m_hDefaultCompletionEvent);
  m_hDefaultCompletionEvent = NULL;
  m_hDevice = NULL;
  m_hPipe = NULL;
  m_pFuncs = NULL;
  m_wMaxPacketSize = 0;
  m_fIsDefaultPipe = FALSE;
  m_pSelectiveSuspend = NULL;

  DEBUGMSG(ZONE_USB_PIPE, (TEXT("HcUsb: -CUsbPipe::Deinitialize(%d) %s\r\n"), fRet, GET_PIPE_STRING(m_bType, m_bIndex)));

  return fRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Transfer data on the USB pipe.
//
// Parameters:
// pData   : The buffer containing or receiving the data.
// dwLen   : The number of bytes to write or read.
// dwTimout: The timeout to be used for the operation.
// fNoZLP  : Flag indicating if there should not be transferred a zero
//           length package or not.
//
// Return:
// The number of actually transferred bytes, -1 indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
DWORD CUsbPipe::TransferData(PBYTE pData, DWORD dwLen, DWORD dwTimeout, BOOL fUseZLP) {
DWORD  dwFlags;
DWORD  dwTransferred = 0;
DWORD  dwWaitReturn = 0;
DWORD  dwUsbErr = USB_NO_ERROR;

  DEBUGMSG(ZONE_USB_TRANSFER, (TEXT("HcUsb: +CUsbPipe::TransferData(%p, %d, %d) on %s (bIdx=%d)\r\n"), pData, dwLen, dwTimeout, GET_PIPE_STRING(m_bType, m_bIndex), m_bIndex));

  // Check input parameter
  if (!dwLen) {
    DEBUGMSG(ZONE_USB_TRANSFER, (TEXT("HcUsb: -CUsbPipe::TransferData(0) on %s - Nothing to transfer, dwLen = 0\r\n"), GET_PIPE_STRING(m_bType, m_bIndex)));
    return 0;
  }
  if (!pData) {
    DEBUGMSG(ZONE_USB_TRANSFER | ZONE_ERROR, (TEXT("HcUsb: -CUsbPipe::TransferData(-1) on %s - Invalid nullpointer passed\r\n"), GET_PIPE_STRING(m_bType, m_bIndex)));
    SetLastError(ERROR_INVALID_PARAMETER);
    return (DWORD)-1;
  }

  DEBUGMSG(ZONE_LOCK, (TEXT("HcUsb:  CUsbPipe::TransferData() - sync: +CUsbPipe (bIdx=%d)\r\n"), m_bIndex));
  SYNCBLOCK(m_csLock);
  DEBUGMSG(ZONE_LOCK, (TEXT("HcUsb:  CUsbPipe::TransferData() - sync: -CUsbPipe (bIdx=%d)\r\n"), m_bIndex));

  // Check if instance is valid
  if (!m_hPipe) {
    DEBUGMSG(ZONE_USB_TRANSFER | ZONE_ERROR, (TEXT("HcUsb: -CUsbPipe::TransferData(-1) on %s - Instance invalid\r\n"), GET_PIPE_STRING(m_bType, m_bIndex)));
    SetLastError(ERROR_INVALID_ACCESS);
    return (DWORD)-1;
  }

  // Allow only one transfer at a time
  if (m_hTransfer) {
    DEBUGMSG(ZONE_USB_TRANSFER | ZONE_ERROR, (TEXT("HcUsb: -CUsbPipe::TransferData(-1) on %s - Transfer already running\r\n"), GET_PIPE_STRING(m_bType, m_bIndex)));
    SetLastError(ERROR_SHARING_VIOLATION);
    return (DWORD)-1;
  }

  if (USB_ENDPOINT_DIRECTION_IN(m_bIndex)) {
    dwFlags = USB_IN_TRANSFER | USB_SHORT_TRANSFER_OK;

    // Wait for a resume on the possibly suspended device
#if defined(USB_DEVICE_SUSPENDED) && defined(USB_DEVICE_RESUMED)
    DWORD dwResumeTimeout = INFINITE;
#else // USB_DEVICE_SUSPENDED && USB_DEVICE_RESUMED
    DWORD dwResumeTimeout = RESUME_POLLING_INTERVAL;
#endif // USB_DEVICE_SUSPENDED && USB_DEVICE_RESUMED

    DEBUGMSG(ZONE_LOCK, (TEXT("HcUsb:  CUsbPipe::TransferData() - sync: -CUsbPipe (bIdx=%d)\r\n"), m_bIndex));
    UNSYNCBLOCK(m_csLock);
    DEBUGMSG(ZONE_LOCK, (TEXT("HcUsb:  CUsbPipe::TransferData() - sync: +CUsbPipe (bIdx=%d)\r\n"), m_bIndex));

    if (!m_pSelectiveSuspend->WaitForResume(dwResumeTimeout,m_hAbortTransferEvent)) {
      ResetEvent( m_hAbortTransferEvent );
      DEBUGMSG(ZONE_USB_TRANSFER, (TEXT("HcUsb: -CUsbPipe::TransferData(-1) - Failed to wait for device resume (closed or aborted)\r\n")));
      return (DWORD)-1;
    }
    if (WaitForSingleObject( m_hAbortTransferEvent, 0 ) == WAIT_OBJECT_0 ) {
      DEBUGMSG(ZONE_USB_TRANSFER, (TEXT("HcUsb: -CUsbPipe::TransferData(-1) - Aborted transfer\r\n")));
      return (DWORD)-1;
    }
#if !defined(USB_DEVICE_SUSPENDED) && !defined(USB_DEVICE_RESUMED)
    if (m_pSelectiveSuspend->IsSuspended()) {
      // if resetting the pipe succeeds the device has came out of suspend mode
      // either by writing data to the device or by remote wakeup from device
      BOOL ret;

      if ( m_fIsDefaultPipe ) {
        ret = m_pFuncs->lpResetDefaultPipe(m_hDevice);
        if ( ret == FALSE ) {
            DEBUGMSG(ZONE_USB_HOSTFN_ERROR, (TEXT("HcUsb: lpResetDefaultPipe() - failed\r\n")));
        }
      } else {
        ret = m_pFuncs->lpResetPipe(m_hPipe);
        if ( ret == FALSE ) {
            DEBUGMSG(ZONE_USB_HOSTFN_ERROR, (TEXT("HcUsb: lpResetPipe() - failed\r\n")));
        }
      }
      if ( ret == FALSE ) {
        DEBUGMSG(ZONE_USB_SUSPEND, (TEXT("HcUsb: CUsbPipe::TransferData() - Device is suspended (bIdx=%d)\r\n"), m_bIndex));
        return (DWORD)-1;
      }
      // the device was suspended, signal now that it is resumed
      DEBUGMSG(ZONE_USB_SUSPEND, (TEXT("HcUsb: CUsbPipe::TransferData() - Device is resumed (bIdx=%d)\r\n"), m_bIndex));
      m_pSelectiveSuspend->DeviceAccessed();
    }
#endif // !defined(USB_DEVICE_SUSPENDED) && !defined(USB_DEVICE_RESUMED)
#ifdef USE_WA_RESET_ON_RESUME
    // now reset the pipe to get it working when the device is resumed
    if (m_pSelectiveSuspend->ResetPipeOnResume( this, m_hPipe, m_bIndex & 0x7F, m_fIsDefaultPipe ) != TRUE) {
      DEBUGMSG(ZONE_USB_TRANSFER, (TEXT("HcUsb: -CUsbPipe::TransferData(-1) - Failed to reset pipe on resume\r\n")));
      return (DWORD)-1;
    }
#endif // USE_WA_RESET_ON_RESUME
  } else {
    dwFlags = USB_OUT_TRANSFER;
    m_pSelectiveSuspend->DeviceAccessed();
  }

  ResetEvent(m_hCompletionEvent);

  switch (m_bType) {
    case USB_ENDPOINT_TYPE_BULK:
      m_hTransfer = m_pFuncs->lpIssueBulkTransfer(m_hPipe, TransferComplete, this, dwFlags, dwLen, pData, 0);
      break;
    case USB_ENDPOINT_TYPE_INTERRUPT:
      m_hTransfer = m_pFuncs->lpIssueInterruptTransfer(m_hPipe, TransferComplete, this, dwFlags, dwLen, pData, 0);
      break;
    default:
      DEBUGMSG(ZONE_USB_TRANSFER | ZONE_ERROR, (TEXT("HcUsb: -CUsbPipe::TransferData(-1) on %s - Unsupported pipe type: %d\r\n"), GET_PIPE_STRING(m_bType, m_bIndex), m_bType));
      SetLastError(ERROR_INVALID_ACCESS);
      return (DWORD)-1;
  }

  if (!m_hTransfer) {
    DWORD dwLastError = GetLastError();
    if (m_pSelectiveSuspend->IsResumed()) {
      if ( Reset(TRUE) == FALSE ) {
        SetLastError(ERROR_CAN_NOT_COMPLETE);
        DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb: -CUsbPipe::TransferData(-1) - Failed to reset %s (bIdx=%d): err = %d\r\n"), GET_PIPE_STRING(m_bType, m_bIndex), m_bIndex, dwLastError));
        return (DWORD)-1;
      }
    }
    if ( dwLastError == ERROR_INVALID_HANDLE ) {
        SetLastError(ERROR_CAN_NOT_COMPLETE);
    } else {
        // re-set last error (may be set to other value due to call to Reset())
        SetLastError(dwLastError);
    }
    DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb: -CUsbPipe::TransferData(-1) - Failed to issue transfer on pipe %s (bIdx=%d): err = %d\r\n"), GET_PIPE_STRING(m_bType, m_bIndex), m_bIndex, dwLastError));
    return (DWORD)-1;
  }

  // Synchronize the transfer completion timer
  {
    DEBUGMSG(ZONE_LOCK, (TEXT("HcUsb:  CUsbPipe::TransferData() - sync: -CUsbPipe id=%d\r\n"), m_bIndex&0x7F));
    UNSYNCBLOCK(m_csLock);
    DEBUGMSG(ZONE_LOCK, (TEXT("HcUsb:  CUsbPipe::TransferData() - sync: +CUsbPipe id=%d\r\n"), m_bIndex&0x7F));

    dwWaitReturn = WaitForSingleObject(m_hCompletionEvent, dwTimeout);
  }

  // We might have been deinitialized meanwhile
  if (!m_hPipe) {
    SetLastError(ERROR_GEN_FAILURE);
    DEBUGMSG(ZONE_USB_TRANSFER | ZONE_ERROR, (TEXT("HcUsb: -CUsbPipe::TransferData(-1) - Pipe deinitialized while waiting for transfer\r\n")));
    return (DWORD)-1;
  }

  switch (dwWaitReturn) {
    case WAIT_OBJECT_0:
      if ( m_pFuncs->lpGetTransferStatus(m_hTransfer, &dwTransferred, &dwUsbErr) == FALSE ) {
          DEBUGMSG(ZONE_USB_HOSTFN_ERROR, (TEXT("HcUsb: lpGetTransferStatus() - failed\r\n")));
      }
      DEBUGMSG(ZONE_USB_TRANSFER, (TEXT("HcUsb:  CUsbPipe::TransferData() %s (bIdx=%d) - %d Bytes transferred\r\n"), GET_PIPE_STRING(m_bType, m_bIndex), m_bIndex, dwTransferred));
      if ( m_pFuncs->lpCloseTransfer(m_hTransfer) == FALSE ) {
          DEBUGMSG(ZONE_USB_HOSTFN_ERROR, (TEXT("HcUsb: lpCloseTransfer() - failed\r\n")));
      }
      m_hTransfer = NULL;
      if (!dwTransferred) {
        if (m_pSelectiveSuspend->IsResumed()) {
          switch (dwUsbErr) {
          case USB_CANCELED_ERROR:
            DEBUGMSG(ZONE_USB_TRANSFER | ZONE_ERROR, (TEXT("HcUsb: CUsbPipe::TransferData() - transfer canceled (bIdx=%d)\r\n"), m_bIndex));
            break;
          case USB_STALL_ERROR:
            DEBUGMSG(ZONE_USB_TRANSFER | ZONE_ERROR, (TEXT("HcUsb: CUsbPipe::TransferData() - clear stall\r\n")));
            Reset(TRUE);
            break;
          case USB_DEVICE_NOT_RESPONDING_ERROR:
            DEBUGMSG(ZONE_USB_TRANSFER | ZONE_ERROR, (TEXT("HcUsb: CUsbPipe::TransferData() - device not responding error on %s (bIdx=%d)\r\n"), GET_PIPE_STRING(m_bType, m_bIndex), m_bIndex));
            dwTransferred = (DWORD)-1;
            break;
          default:
            DEBUGMSG(ZONE_USB_TRANSFER | ZONE_ERROR, (TEXT("HcUsb: CUsbPipe::TransferData() - unhandled USB error: %d on %s (bIdx=%d)\r\n"), dwUsbErr, GET_PIPE_STRING(m_bType, m_bIndex), m_bIndex));
            dwTransferred = (DWORD)-1;
            break;
          }
        }
        else {
          if (m_pSelectiveSuspend->IsSuspended()) {
            DEBUGMSG(ZONE_INFO & (ZONE_USB_TRANSFER | ZONE_USB_SUSPEND), (TEXT("HcUsb:  CUsbPipe::TransferData() %s - %d Bytes transferred and device is supended (err: %d)\r\n"), GET_PIPE_STRING(m_bType, m_bIndex), dwTransferred, dwUsbErr));
          }
        }
      } else {
        if (fUseZLP && USB_ENDPOINT_DIRECTION_OUT(m_bIndex)) {
          switch (m_bType) {
            case USB_ENDPOINT_TYPE_BULK:
              // see: USB 2.0 Spec - 5.8.3 "Bulk Transfer Packet Size Constraints"
              if ((dwTransferred % m_wMaxPacketSize) == 0) {
                TransferZeroLengthPacket();
              }
              break;
            case USB_ENDPOINT_TYPE_INTERRUPT:
              // see: USB 2.0 Spec - 5.7.3 "Interrupt Transfer Packet Size Constraints"
              // TODO: untested code - currently there is no module that uses interrupt out endpoints.
              DEBUGMSG(ZONE_ERROR, (TEXT("HcUsb: CUsbPipe::TransferData() - unexpected ZLP on interrupt out pipe (bIdx=%d)\r\n"), m_bIndex));
              if (((dwTransferred % m_wMaxPacketSize) == 0) && (dwLen != dwTransferred)) {
                TransferZeroLengthPacket();
              }
              break;
          }
        }
        if ((dwTransferred != dwLen) && (!USB_ENDPOINT_DIRECTION_IN(m_bIndex))) {
          DEBUGMSG(ZONE_USB_TRANSFER, (TEXT("HcUsb: CUsbPipe::TransferData() - %d of %d bytes were transmitted on %s (bIdx=%d)\r\n"), dwTransferred, dwLen, GET_PIPE_STRING(m_bType, m_bIndex), m_bIndex));
        }
        if (USB_ENDPOINT_DIRECTION_IN(m_bIndex)) {
          m_pSelectiveSuspend->DeviceAccessed();
        }
      }
      break;
    case WAIT_TIMEOUT:
      DEBUGMSG(ZONE_USB_TRANSFER, (TEXT("HcUsb:  CUsbPipe::TransferData() on %s - Timed out\r\n"), GET_PIPE_STRING(m_bType, m_bIndex)));
      AbortTransfer();
      WaitForSingleObject(m_hCompletionEvent, 2000);
      if ( m_pFuncs->lpGetTransferStatus(m_hTransfer, &dwTransferred, &dwUsbErr) == FALSE ) {
          DEBUGMSG(ZONE_USB_HOSTFN_ERROR, (TEXT("HcUsb: lpGetTransferStatus() - failed\r\n")));
      }
      if ( m_pFuncs->lpCloseTransfer(m_hTransfer) == FALSE ) {
          DEBUGMSG(ZONE_USB_HOSTFN_ERROR, (TEXT("HcUsb: lpCloseTransfer() - failed\r\n")));
      }
      m_hTransfer = NULL;
      Reset(TRUE);
      break;
    default:
      DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb: CUsbPipe::TransferData() - Unhandled Wait-Return: %d\r\n"), dwWaitReturn));
      if ( m_pFuncs->lpGetTransferStatus(m_hTransfer, &dwTransferred, &dwUsbErr) == FALSE ) {
          DEBUGMSG(ZONE_USB_HOSTFN_ERROR, (TEXT("HcUsb: lpGetTransferStatus() - failed\r\n")));
      }
      if ( m_pFuncs->lpCloseTransfer(m_hTransfer) == FALSE ) {
          DEBUGMSG(ZONE_USB_HOSTFN_ERROR, (TEXT("HcUsb: lpCloseTransfer() - failed\r\n")));
      }
      m_hTransfer = NULL;
      Reset(TRUE);
      SetLastError(ERROR_GEN_FAILURE);
      dwTransferred = (DWORD)-1;
      break;
  }

  DEBUGMSG(ZONE_USB_TRANSFER, (TEXT("HcUsb: -CUsbPipe::TransferData(%d) on %s (bIdx=%d)\r\n"), dwTransferred, GET_PIPE_STRING(m_bType, m_bIndex), m_bIndex));
  return dwTransferred;
}

//////////////////////////////////////////////////////////////////////////////
//
// Transfer vendor data on the USB pipe.
//
// Parameters:
// fOut    : Flag indicating if it is a read (FALSE) or write (TRUE) transfer.
// wIndex  : Interface number of the interface the transfer belongs to.
// bRequest: Request type of the vendor transfer.
// wValue  : Value to be transferred.
// pData   : The buffer containing or receiving the data.
// dwLen   : The number of bytes to write or read.
// dwTimout: The timeout to be used for the operation.
//
// Return:
// The number of actually transferred bytes, -1 indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
DWORD CUsbPipe::TransferVendorData(BOOL fOut, USHORT wIndex, UCHAR bRequest, USHORT wValue, PBYTE pData, USHORT wLen, DWORD dwTimeout) {
USB_DEVICE_REQUEST  CtrlHeader;
DWORD               dwFlags;
DWORD               dwTransferred = 0;
DWORD               dwWaitReturn = 0;
DWORD               dwUsbErr = USB_NO_ERROR;

  DEBUGMSG(ZONE_USB_TRANSFER, (TEXT("HcUsb: +CUsbPipe::TransferVendorData(%d, %d, %x, %d, %p, %d, %d) on %s\r\n"), fOut, wIndex, bRequest, wValue, pData, wLen, dwTimeout, GET_PIPE_STRING(m_bType, m_bIndex)));

  // Check input parameter
  if (wLen && !pData) {
    DEBUGMSG(ZONE_USB_TRANSFER | ZONE_ERROR, (TEXT("HcUsb: -CUsbDefaultPipe::TransferVendorData(-1) %s - Invalid nullpointer passed\r\n"), GET_DIRECTION_STRING(fOut)));
    SetLastError(ERROR_INVALID_PARAMETER);
    return (DWORD)-1;
  }

  DEBUGMSG(ZONE_LOCK, (TEXT("HcUsb: -CUsbDefaultPipe::TransferVendorData() %s - sync: +CUsbPipe\r\n"), GET_DIRECTION_STRING(fOut)));
  SYNCBLOCK(m_csLock);
  DEBUGMSG(ZONE_LOCK, (TEXT("HcUsb: -CUsbDefaultPipe::TransferVendorData() %s - sync: -CUsbPipe\r\n"), GET_DIRECTION_STRING(fOut)));

  // Wait until there is no more ongoing transfer
  while (m_hTransfer && m_fIsDefaultPipe) {
    UNSYNCBLOCK(m_csLock);
    if (WaitForSingleObject(m_hDefaultCompletionEvent, dwTimeout) != WAIT_OBJECT_0) {
      DEBUGMSG(ZONE_USB_TRANSFER | ZONE_ERROR, (TEXT("HcUsb: -CUsbPipe::TransferVendorData() %s - Timout waiting for ongoing transfer\r\n"), GET_DIRECTION_STRING(fOut)));
      return (DWORD)-1;
    }
  }

  // Check if instance is valid
  if (!m_fIsDefaultPipe) {
    DEBUGMSG(ZONE_USB_TRANSFER | ZONE_ERROR, (TEXT("HcUsb: -CUsbPipe::TransferVendorData(-1) %s - Instance invalid\r\n"), GET_DIRECTION_STRING(fOut)));
    SetLastError(ERROR_INVALID_ACCESS);
    return (DWORD)-1;
  }

  if (fOut) {
    dwFlags = USB_OUT_TRANSFER;
    CtrlHeader.bmRequestType = USB_REQUEST_HOST_TO_DEVICE | USB_REQUEST_CLASS | USB_REQUEST_FOR_INTERFACE;
    m_pSelectiveSuspend->DeviceAccessed();
  } else {
    dwFlags = USB_IN_TRANSFER | USB_SHORT_TRANSFER_OK;
    CtrlHeader.bmRequestType = USB_REQUEST_DEVICE_TO_HOST | USB_REQUEST_CLASS | USB_REQUEST_FOR_INTERFACE;

    // Wait for a resume on the possibly suspended device
#if defined(USB_DEVICE_SUSPENDED) && defined(USB_DEVICE_RESUMED)
    DWORD dwResumeTimeout = INFINITE;
#else // USB_DEVICE_SUSPENDED && USB_DEVICE_RESUMED
    DWORD dwResumeTimeout = RESUME_POLLING_INTERVAL;
#endif // USB_DEVICE_SUSPENDED && USB_DEVICE_RESUMED
    UNSYNCBLOCK(m_csLock);
    if (!m_pSelectiveSuspend->WaitForResume(dwResumeTimeout,m_hAbortTransferEvent)) {
      ResetEvent( m_hAbortTransferEvent );
      DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb: -CUsbPipe::TransferVendorData(-1) - Failed to wait for device resume\r\n")));
      return (DWORD)-1;
    }
    if (WaitForSingleObject( m_hAbortTransferEvent, 0 ) == WAIT_OBJECT_0 ) {
      DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb: -CUsbPipe::TransferVendorData(-1) - Aborted transfer\r\n")));
      return (DWORD)-1;
    }
  }

  CtrlHeader.bRequest  = bRequest;
  CtrlHeader.wIndex    = wIndex;
  CtrlHeader.wValue    = wValue;
  CtrlHeader.wLength   = wLen;

  ResetEvent(m_hCompletionEvent);
  ResetEvent(m_hDefaultCompletionEvent);

  DEBUGMSG(ZONE_USB_HOSTFN, (TEXT("HcUsb: +lpIssueVendorTransfer()\r\n")));
  m_hTransfer = m_pFuncs->lpIssueVendorTransfer(m_hDevice, TransferComplete, this, dwFlags, &CtrlHeader, pData, 0);
  if (m_hTransfer == NULL ) {
      DEBUGMSG(ZONE_USB_HOSTFN_ERROR, (TEXT("HcUsb: lpIssueVendorTransfer() - failed\r\n")));
  }
  DEBUGMSG(ZONE_USB_HOSTFN, (TEXT("HcUsb: -lpIssueVendorTransfer(%s)\r\n"), m_hTransfer!=NULL?TEXT("TRUE"):TEXT("FALSE")));

  if (!m_hTransfer) {
    DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb: -CUsbPipe::TransferVendorData(-1) %s - Failed to issue transfer: %d\r\n"), GET_DIRECTION_STRING(fOut), GetLastError()));
    if (m_pSelectiveSuspend->IsResumed()) {
      Reset(TRUE);
    }
    SetEvent(m_hDefaultCompletionEvent);
    return (DWORD)-1;
  }

  // Synchronize the transfer completion timer
  {
    UNSYNCBLOCK(m_csLock);
    dwWaitReturn = WaitForSingleObject(m_hCompletionEvent, dwTimeout);
  }

  // We might have been deinitialized meanwhile
  if (!m_hDevice) {
    DEBUGMSG(ZONE_USB_TRANSFER, (TEXT("HcUsb: -CUsbPipe::TransferVendorData(-1) %s - Pipe deinitialized while waiting for transfer\r\n"), GET_DIRECTION_STRING(fOut)));
    SetLastError(ERROR_GEN_FAILURE);
    SetEvent(m_hDefaultCompletionEvent);
    return (DWORD)-1;
  }

  switch (dwWaitReturn) {
    case WAIT_OBJECT_0:
      if ( m_pFuncs->lpGetTransferStatus(m_hTransfer, &dwTransferred, &dwUsbErr) == FALSE ) {
          DEBUGMSG(ZONE_USB_HOSTFN_ERROR, (TEXT("HcUsb: lpGetTransferStatus() - failed\r\n")));
      }
      DEBUGMSG(ZONE_USB_TRANSFER, (TEXT("HcUsb:  CUsbPipe::TransferVendorData() %s - %d Bytes transferred\r\n"), GET_DIRECTION_STRING(fOut), dwTransferred));
      if ( m_pFuncs->lpCloseTransfer(m_hTransfer) == FALSE ) {
          DEBUGMSG(ZONE_USB_HOSTFN_ERROR, (TEXT("HcUsb: lpCloseTransfer() - failed\r\n")));
      }
      m_hTransfer = NULL;
      if (!dwTransferred && m_pSelectiveSuspend->IsResumed()) {
        Reset(TRUE);
      }
      if (dwTransferred && !fOut) {
        m_pSelectiveSuspend->DeviceAccessed();
      }
      break;
    case WAIT_TIMEOUT:
      AbortTransfer();
      WaitForSingleObject(m_hCompletionEvent, 2000);
      if ( m_pFuncs->lpGetTransferStatus(m_hTransfer, &dwTransferred, &dwUsbErr) == FALSE ) {
          DEBUGMSG(ZONE_USB_HOSTFN_ERROR, (TEXT("HcUsb: lpGetTransferStatus() - failed\r\n")));
      }
      if ( m_pFuncs->lpCloseTransfer(m_hTransfer) == FALSE ) {
          DEBUGMSG(ZONE_USB_HOSTFN_ERROR, (TEXT("HcUsb: lpCloseTransfer() - failed\r\n")));
      }
      m_hTransfer = NULL;
      Reset(TRUE);
      DEBUGMSG(ZONE_USB_TRANSFER, (TEXT("HcUsb:  CUsbPipe::TransferVendorData() %s - Timed out, %d Bytes transferred\r\n"), GET_DIRECTION_STRING(fOut), dwTransferred));
      break;
    default:
      DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb: CUsbPipe::TransferVendorData() %s - Unhandled Wait-Return: %d\r\n"), GET_DIRECTION_STRING(fOut), dwWaitReturn));
      if ( m_pFuncs->lpGetTransferStatus(m_hTransfer, &dwTransferred, &dwUsbErr) == FALSE ) {
          DEBUGMSG(ZONE_USB_HOSTFN_ERROR, (TEXT("HcUsb: lpGetTransferStatus() - failed\r\n")));
      }
      if ( m_pFuncs->lpCloseTransfer(m_hTransfer) == FALSE ) {
          DEBUGMSG(ZONE_USB_HOSTFN_ERROR, (TEXT("HcUsb: lpCloseTransfer() - failed\r\n")));
      }
      m_hTransfer = NULL;
      Reset(TRUE);
      SetLastError(ERROR_GEN_FAILURE);
      dwTransferred = (DWORD)-1;
      break;
  }

  SetEvent(m_hDefaultCompletionEvent);

  DEBUGMSG(ZONE_USB_TRANSFER, (TEXT("HcUsb: -CUsbPipe::TransferVendorData(%d) %s\r\n"), dwTransferred, GET_DIRECTION_STRING(fOut)));

  return dwTransferred;
}

BOOL CUsbPipe::TransferZeroLengthPacket(void)
{
  DWORD  dwFlags;
  DWORD  dwTransferred = 0;
  DWORD  dwWaitReturn = 0;
  DWORD  dwUsbErr = USB_NO_ERROR;

  DEBUGMSG(ZONE_USB_ZLP | ZONE_USB_TRANSFER, (TEXT("HcUsb: +CUsbPipe::TransferZeroLengthPacket() on %s (bIdx=%d)\r\n"), GET_PIPE_STRING(m_bType, m_bIndex), m_bIndex));

  DEBUGMSG(ZONE_LOCK, (TEXT("HcUsb:  CUsbPipe::TransferZeroLengthPacket() - sync: +CUsbPipe (bIdx=%d)\r\n"), m_bIndex));
  SYNCBLOCK(m_csLock);
  DEBUGMSG(ZONE_LOCK, (TEXT("HcUsb:  CUsbPipe::TransferZeroLengthPacket() - sync: -CUsbPipe (bIdx=%d)\r\n"), m_bIndex));

  // Check if instance is valid
  if (!m_hPipe) {
    DEBUGMSG(ZONE_USB_ZLP | ZONE_USB_TRANSFER | ZONE_ERROR,
       (TEXT("HcUsb: -CUsbPipe::TransferZeroLengthPacket(-1) on %s - Instance invalid\r\n"), GET_PIPE_STRING(m_bType, m_bIndex)));
    SetLastError(ERROR_INVALID_ACCESS);
    return FALSE;
  }

  // Allow only one transfer at a time
  if (m_hTransfer) {
    DEBUGMSG(ZONE_USB_ZLP | ZONE_USB_TRANSFER | ZONE_ERROR, (TEXT("HcUsb: -CUsbPipe::TransferData(-1) on %s - Transfer already running\r\n"), GET_PIPE_STRING(m_bType, m_bIndex)));
    SetLastError(ERROR_SHARING_VIOLATION);
    return FALSE;
  }

  if (USB_ENDPOINT_DIRECTION_IN(m_bIndex)) {
    DEBUGMSG(ZONE_USB_ZLP| ZONE_ERROR, (TEXT("HcUsb:  -CUsbPipe::TransferZeroLengthPacket(0) - ZLP on IN pipe.\r\n"), m_bIndex));
    return FALSE;
  }
  else
  {
    dwFlags = USB_OUT_TRANSFER;
  }

  ResetEvent(m_hCompletionEvent);

  switch (m_bType) {
    case USB_ENDPOINT_TYPE_BULK:
      m_hTransfer = m_pFuncs->lpIssueBulkTransfer(m_hPipe, TransferComplete, this, USB_OUT_TRANSFER, 0, NULL, 0);
      if ( m_hTransfer == NULL ) {
        DEBUGMSG(ZONE_USB_ZLP|ZONE_ERROR, (TEXT("HcUsb: CUsbPipe::TransferZeroLengthPacket() - lpIssueBulkTransfer() failed\r\n")));
      }
      break;

    case USB_ENDPOINT_TYPE_INTERRUPT:
      m_hTransfer = m_pFuncs->lpIssueInterruptTransfer(m_hPipe, TransferComplete, this, USB_OUT_TRANSFER, 0, NULL, 0);
      if ( m_hTransfer == NULL ) {
        DEBUGMSG(ZONE_USB_ZLP|ZONE_ERROR, (TEXT("HcUsb: CUsbPipe::TransferZeroLengthPacket() - lpIssueInterruptTransfer() failed\r\n")));
      }
      break;

    default:
      DEBUGMSG(ZONE_USB_ZLP|ZONE_ERROR, (TEXT("HcUsb: -CUsbPipe::TransferZeroLengthPacket(0) on %s - Unsupported pipe type: %d\r\n"), GET_PIPE_STRING(m_bType, m_bIndex), m_bType));
      break;
  }

  if (!m_hTransfer) {
    DEBUGMSG(ZONE_USB_ZLP | ZONE_ERROR, (TEXT("HcUsb: -CUsbPipe::TransferZeroLengthPacket(0) - Failed to issue transfer on pipe %s (bIdx=%d)\r\n"), GET_PIPE_STRING(m_bType, m_bIndex), m_bIndex));
    return FALSE;
  }

  // Synchronize the transfer completion timer
  {
    DEBUGMSG(ZONE_LOCK, (TEXT("HcUsb:  CUsbPipe::TransferZeroLengthPacket() - sync: -CUsbPipe id=%d\r\n"), m_bIndex&0x7F));
    UNSYNCBLOCK(m_csLock);
    DEBUGMSG(ZONE_LOCK, (TEXT("HcUsb:  CUsbPipe::TransferZeroLengthPacket() - sync: +CUsbPipe id=%d\r\n"), m_bIndex&0x7F));

    dwWaitReturn = WaitForSingleObject(m_hCompletionEvent, INFINITE);
  }

  switch (dwWaitReturn) {
  case WAIT_OBJECT_0:
    if ( m_pFuncs->lpGetTransferStatus(m_hTransfer, &dwTransferred, &dwUsbErr) == FALSE ) {
      DEBUGMSG(ZONE_USB_HOSTFN_ERROR, (TEXT("HcUsb: lpGetTransferStatus() - failed\r\n")));
    }
    else
    {
      DEBUGMSG(ZONE_USB_TRANSFER, (TEXT("HcUsb:  CUsbPipe::TransferZeroLengthPacket() %s (bIdx=%d) - %d Bytes transferred\r\n"), GET_PIPE_STRING(m_bType, m_bIndex), m_bIndex, dwTransferred));
      switch (dwUsbErr) {
      case USB_NO_ERROR:
        break;
      case USB_CANCELED_ERROR:
        DEBUGMSG(ZONE_USB_TRANSFER | ZONE_ERROR,
            (TEXT("HcUsb: CUsbPipe::TransferZeroLengthPacket() - transfer canceled (bIdx=%d)\r\n"), m_bIndex));
        break;
      case USB_STALL_ERROR:
        DEBUGMSG(ZONE_USB_TRANSFER | ZONE_ERROR, (TEXT("HcUsb: CUsbPipe::TransferZeroLengthPacket() - stall error\r\n")));
        break;
      default:
        DEBUGMSG(ZONE_USB_TRANSFER | ZONE_ERROR,
            (TEXT("HcUsb: CUsbPipe::TransferZeroLengthPacket() - unhandled USB error: %d on %s (bIdx=%d)\r\n"), dwUsbErr, GET_PIPE_STRING(m_bType, m_bIndex), m_bIndex));
        break;
      }

    }

    if ( m_pFuncs->lpCloseTransfer(m_hTransfer) == FALSE ) {
        DEBUGMSG(ZONE_USB_HOSTFN_ERROR, (TEXT("HcUsb: lpCloseTransfer() - failed\r\n")));
    }
    m_hTransfer = NULL;
    break;

  default:
    break;
  }

  DEBUGMSG(ZONE_USB_ZLP, (TEXT("HcUsb: -CUsbPipe::TransferZeroLengthPacket(TRUE) %s\r\n"), GET_PIPE_STRING(m_bType, m_bIndex)));

  return TRUE;
}

BOOL CUsbPipe::Open(void) {
    ResetEvent(m_hAbortTransferEvent);
    return TRUE;
}

BOOL CUsbPipe::Close(void) {
    return TRUE;
}


//////////////////////////////////////////////////////////////////////////////
//
// Abort possible ongoing data transfer.
//
// Parameters:
// None.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CUsbPipe::AbortTransfer(void) {
BOOL fRet;

  DEBUGMSG(ZONE_USB_TRANSFER, (TEXT("HcUsb: +CUsbPipe::AbortTransfer() on %s (bType=%d, bIdx=%d)\r\n"), GET_PIPE_STRING(m_bType, m_bIndex), m_bType, m_bIndex));

  DEBUGMSG(ZONE_LOCK, (TEXT("HcUsb:  CUsbPipe::AbortTransfer() - sync: +CUsbPipe (bIdx=%d)\r\n"), m_bIndex));
  SYNCBLOCK(m_csLock);
  DEBUGMSG(ZONE_LOCK, (TEXT("HcUsb:  CUsbPipe::AbortTransfer() - sync: -CUsbPipe (bIdx=%d)\r\n"), m_bIndex));

  if (!m_fIsDefaultPipe && !m_hPipe) {
    SetLastError(ERROR_INVALID_ACCESS);
    DEBUGMSG(ZONE_USB_TRANSFER, (TEXT("HcUsb: -CUsbPipe::AbortTransfer(0) on %s - Instance invalid\r\n"), GET_PIPE_STRING(m_bType, m_bIndex)));
    return FALSE;
  }

  // set event to abort a possible wait-for-resume on IN pipes
  if (m_pSelectiveSuspend->IsSuspended() && USB_ENDPOINT_DIRECTION_IN(m_bIndex) ) {
      DEBUGMSG(ZONE_USB_TRANSFER, (TEXT("HcUsb:  CUsbPipe::AbortTransfer() on %s - set abort event\r\n"), GET_PIPE_STRING(m_bType, m_bIndex)));
      SetEvent(m_hAbortTransferEvent);
  }

  if (!m_hTransfer) {
    DEBUGMSG(ZONE_USB_TRANSFER, (TEXT("HcUsb: -CUsbPipe::AbortTransfer(1) on %s - No transfer running\r\n"), GET_PIPE_STRING(m_bType, m_bIndex)));
    return TRUE;
  }
  m_pSelectiveSuspend->DeviceAccessed();
  fRet = m_pFuncs->lpAbortTransfer(m_hTransfer, 0);
  if ( fRet == FALSE ) {
      DEBUGMSG(ZONE_USB_HOSTFN_ERROR, (TEXT("HcUsb: lpAbortTransfer() - failed\r\n")));
  }

  Reset(FALSE);

  DEBUGMSG(ZONE_USB_TRANSFER, (TEXT("HcUsb: -CUsbPipe::AbortTransfer(%d) on %s (bIdx=%d)\r\n"), fRet, GET_PIPE_STRING(m_bType, m_bIndex), m_bIndex));
  return fRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Reset the pipe.
//
// Parameters:
// fClrStall: Flag indicating if ClearFeature(STALL) shall be send to the
//            endpoint. This should only be done in case of an unrecoverable
//            error condition.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CUsbPipe::Reset(BOOL fClrStall) {
BOOL fRet = TRUE;

  DEBUGMSG(ZONE_USB_TRANSFER, (TEXT("HcUsb: +CUsbPipe::Reset(%d) on %s\r\n"), fClrStall, GET_PIPE_STRING(m_bType, m_bIndex)));
  DEBUGMSG(ZONE_LOCK, (TEXT("HcUsb:  CUsbPipe::Reset() sync: +CUsbPipe on %s\r\n"), GET_PIPE_STRING(m_bType, m_bIndex)));
  SYNCBLOCK(m_csLock);
  DEBUGMSG(ZONE_LOCK, (TEXT("HcUsb:  CUsbPipe::Reset() sync: -CUsbPipe on %s\r\n"), GET_PIPE_STRING(m_bType, m_bIndex)));
  if (!m_fIsDefaultPipe && !m_hPipe) {
    SetLastError(ERROR_INVALID_ACCESS);
    DEBUGMSG(ZONE_USB_TRANSFER|ZONE_ERROR, (TEXT("HcUsb: -CUsbPipe::Reset(0) on %s - Instance invalid\r\n"), GET_PIPE_STRING(m_bType, m_bIndex)));
    return FALSE;
  }
  m_pSelectiveSuspend->DeviceAccessed();

  if (m_fIsDefaultPipe) {
    DEBUGMSG(ZONE_USB_HOSTFN, (TEXT("HcUsb: +lpResetDefaultPipe()\r\n")));

 #ifdef USE_WATCHDOG_RESET_PIPE
    WCHAR wszMessage[128];
    wsprintf( wszMessage, L"lpResetDefaultPipe() on pipe %d", m_bIndex & 0x7F );
    m_pWDResetPipe->start( wszMessage );
 #endif
    if (m_pFuncs->lpResetDefaultPipe(m_hDevice) == FALSE ) {
        DEBUGMSG(ZONE_USB_HOSTFN_ERROR, (TEXT("HcUsb: lpResetDefaultPipe() - failed\r\n")));
        fRet = FALSE;
    }
 #ifdef USE_WATCHDOG_RESET_PIPE
    m_pWDResetPipe->stop();
 #endif
    DEBUGMSG(ZONE_USB_HOSTFN, (TEXT("HcUsb: -lpResetDefaultPipe(%s)\r\n"), fRet?TEXT("TRUE"):TEXT("FALSE")));
    if (fClrStall) {
      /*
        If the default pipe is stalled, it gets unstalled after the next SETUP transaction in case of a protocol stall.
        A device must automatically un-stall EP0 as soon as another SETUP packet is received on it. Any stall on EP0 is
        considered a "protocol STALL" rather than a "functional STALL" (see the USB spec. chap. 8.5.3.4 for details).

        Do nothing here ...
      */
    }
  } else {
    DEBUGMSG(ZONE_USB_HOSTFN, (TEXT("HcUsb: +lpAbortPipeTransfers() on pipe %d\r\n"), m_bIndex & 0x7F));
    if (!m_pFuncs->lpAbortPipeTransfers(m_hPipe, 0)) {
        DEBUGMSG(ZONE_USB_HOSTFN_ERROR, (TEXT("HcUsb: lpAbortPipeTransfers() - failed\r\n")));
        fRet = FALSE;
    }
    DEBUGMSG(ZONE_USB_HOSTFN, (TEXT("HcUsb: -lpAbortPipeTransfers(%s)\r\n"), fRet?TEXT("TRUE"):TEXT("FALSE")));

    // Reset blocks if we had vendor in transfers
    DEBUGMSG(ZONE_USB_HOSTFN, (TEXT("HcUsb: +lpResetPipe() on pipe %d\r\n"), m_bIndex & 0x7F));
 #ifdef USE_WATCHDOG_RESET_PIPE
    WCHAR wszMessage[128];
    wsprintf( wszMessage, L"lpResetPipe() on pipe %d", m_bIndex & 0x7F );
    m_pWDResetPipe->start(wszMessage);
 #endif
    if (!m_pFuncs->lpResetPipe(m_hPipe)) {
        DEBUGMSG(ZONE_USB_HOSTFN_ERROR, (TEXT("HcUsb: lpResetPipe() - failed\r\n")));
        fRet = FALSE;
    }
 #ifdef USE_WATCHDOG_RESET_PIPE
    m_pWDResetPipe->stop();
 #endif
    DEBUGMSG(ZONE_USB_HOSTFN, (TEXT("HcUsb: -lpResetPipe(%s)\r\n"), fRet?TEXT("TRUE"):TEXT("FALSE")));
    if (fClrStall) {
      USB_TRANSFER hTransfer = m_pFuncs->lpClearFeature(m_hPipe, NULL, NULL, USB_SEND_TO_ENDPOINT, USB_FEATURE_ENDPOINT_STALL, m_bIndex);
      if (hTransfer) {
        if ( m_pFuncs->lpCloseTransfer(hTransfer) == FALSE ) {
            DEBUGMSG(ZONE_USB_HOSTFN_ERROR, (TEXT("HcUsb: lpCloseTransfer() - failed\r\n")));
        }
      } else {
        DEBUGMSG(ZONE_USB_HOSTFN_ERROR, (TEXT("HcUsb: lpClearFeature() - failed\r\n")));
        fRet = FALSE;
      }
    }
  }
  DEBUGMSG(ZONE_USB_TRANSFER, (TEXT("HcUsb: -CUsbPipe::Reset(%d) on %s\r\n"), fRet, GET_PIPE_STRING(m_bType, m_bIndex)));
  return fRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// CUsbPipe - Private implementation
//
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//
// Callback function for end of USB transfer.
//
// Parameters:
// pvNotifyParameter: The backward pointer to our class instance.
//
// Return:
// ERROR_SUCCESS on success. Windows error code on failure.
//
//////////////////////////////////////////////////////////////////////////////
DWORD CUsbPipe::TransferComplete(PVOID pvNotifyParameter) {
CUsbPipe*  pInstance = (CUsbPipe*)pvNotifyParameter;
DWORD      dwErr  = ERROR_SUCCESS;

  DEBUGMSG(ZONE_USB_TRANSFER, (TEXT("HcUsb: +CUsbPipe::TransferComplete() on %s\r\n"), GET_PIPE_STRING(pInstance->m_bType, pInstance->m_bIndex)));
  if (!SetEvent(pInstance->m_hCompletionEvent)) {
    dwErr = GetLastError();
    DEBUGMSG(ZONE_USB_TRANSFER | ZONE_ERROR,(TEXT("HcUsb: CUsbPipe::TransferComplete() - SetEvent ERROR: %d\r\n"), dwErr));
  }
  DEBUGMSG(ZONE_USB_TRANSFER, (TEXT("HcUsb: -CUsbPipe::TransferComplete(%d) on %s\r\n"), dwErr, GET_PIPE_STRING(pInstance->m_bType, pInstance->m_bIndex)));
  return dwErr;
}

