/*****************************************************************************
 * 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/SelectiveSuspend.cpp $
 * $Revision: /main/ms-d/40 $
 * $Date: 2013-01-10 15:09:02 $
 * $Author: thinze $
 *****************************************************************************
 * Target:
 *  Microsoft Windows CE 5.0
 *****************************************************************************
 * Description:
 *  Quinn-USB-Driver: Implementation of the class CSelectiveSuspend
 *  doing the USB selective suspend handling.
 *****************************************************************************
 *
 * $Log: E:/cc/thinze_ms-d_snap/z_ms-d/src/working/Quinn/HcUsbDrv/SelectiveSuspend.cpp$ 
 * 
 * /main/ms-d/40   2013-01-10 15:09:02   thinze 
 * added new debug messages to trace host function calls 
 * 
 * /main/ms-d/39   2012-11-08 13:04:36   thinze 
 * improved error logging 
 * 
 * /main/ms-d/38   2012-10-24 15:46:44   mwohler 
 * Fixed typo 
 * 
 * /main/ms-d/37   2012-09-21 13:42:33   mwohler 
 * Fixed memory leak by missing CloseHandle() call 
 * 
 * /main/ms-d/36   2012-04-23 14:47:01   ctna0152 
 * bugfix - trigger selective suspend handling if resuming was done (in case application is not accessing port after resume for a while) 
 * 
 * /main/ms-d/35   2012-03-01 16:09:41   ctna0152 
 * bugfix - trigger selective suspend handling if suspending was aborted due to remote wakeup 
 * 
 * /main/ms-d/34   2011-02-02 10:27:42   ctna0450 
 * Removed unused USB-Reset workaround for Uranus USB problems
 *
 * /main/ms-d/33   2011-01-13 15:23:15   ctna0152
 * bugfix - sometimes SSR handling was not triggered again on remote wakeup
 *
 * /main/ms-d/32   2010-12-13 14:21:41   ctna0152
 * added USB reset handling
 *
 * /main/ms-d/31   2010-11-15 17:49:48   ctna0152
 * added workaround "PAUSE_ON_RESUME" for IPIS100068602
 *
 * /main/ms-d/30   2010-02-18 14:56:51   ctna0152
 * bugfix for suspending device (if polling mode)
 *
 * /main/ms-d/29   2009-02-23 12:47:56   ctna0450
 * Increase thread priority on close to consider high system load
 *
 * /main/ms-d/28   2009-02-18 11:49:25   ctna0256
 * Set copyright text
 *
 * /main/ms-d/27   2008-08-20 11:39:46   ctna0450
 * Prepared for new disclaimer handling
 *
 * /main/ms-d/26   2008-04-22 13:42:13   ht37010
 * added check for NULL pointer
 *
 * /main/ms-d/25   2008-04-22 10:17:12   ht37010
 * added SYNCBLOCK to avoid destruction of SelectiveSuspend object while ResetPipeOnResume() is running
 *
 * /main/ms-d/24   2008-04-11 19:10:11   ht37010
 * added debug msgs for ZONE_USB_HOSTFN_ERROR
 *
 * /main/ms-d/23   2008-04-10 15:18:36   ht37010
 * changed debug messages in WaitForResume()
 *
 * /main/ms-d/22   2008-03-03 11:19:52   ht37010
 * improved handling of new event in WaitForResume()
 *
 * /main/ms-d/21   2008-02-18 15:12:04   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/20   2007-12-18 14:57:43   ht37010
 * bugifx IPIS100044017
 * added usb watchdog for lpSuspendDevice & lpResetPipe
 *
 * /main/ms-d/19   2007-09-12 12:37:24   ht37010
 * changed debug messages
 *
 * /main/ms-d/18   2007-08-31 13:46:41   ht37010
 * added debug message
 *
 * /main/ms-d/17   2007-08-28 12:50:53   ht37010
 * added debug messages
 *
 * /main/ms-d/16   2007-08-28 11:06:20   ht37010
 * added debug message
 *
 * /main/ms-d/15   2007-08-14 12:44:10   ht37010
 * added UNSYNC in Deinitialize()
 * added debug messages
 *
 * /main/ms-d/14   2007-08-02 15:45:17   ht37010
 * added debug messages for host functions
 *
 * /main/ms-d/13   2007-07-31 11:17:30   ht37010
 * added debug messages
 *
 * /main/ms-d/12   2007-07-17 10:26:35   ht37010
 * removed critical section for pipe-on-reset-workaround
 * changed debug messages
 *
 * /main/ms-d/11   2007-06-01 12:49:19   ht37010
 * added debug message
 * minor changes in suspend / resume handling
 *
 * /main/ms-d/10   2007-05-31 15:42:07   ht37010
 * fixes for suspend / resume notifications
 *
 * /main/ms-d/9   2007-05-30 16:38:37   ht37010
 * wait forever for thread termination
 * encapsulation of WaitForTransition
 * some bugfixes for suspend / resume notifications
 *
 * /main/ms-d/8   2007-05-08 13:06:03   ht37010
 * set suspend-event on resume
 *
 * /main/ms-d/7   2007-04-16 12:23:19   ht37010
 * workaround - reset pipe on resume
 *
 * /main/ms-d/6   2007-02-16 17:17:46   wm36264
 * Added conditional compile to support resume polling without notification interface
 *
 * /main/ms-d/5   2007-02-16 16:37:54   wm36264
 * Adapted selective suspend/resume notification to MS confirmend interface.
 * Untested and still deactivated by default!
 *
 * /main/ms-d/4   2006-12-14 12:15:56   wm36264
 * Prepared resume notification
 *
 * /main/ms-d/3   2006-11-03 17:25:26   wm36264
 * Removed all resume info traces
 *
 * /main/ms-d/2   2006-11-03 17:22:02   wm36264
 * Removed annoying trace
 *
 * /main/ms-d/1   2006-11-03 17:05:00   wm36264
 * Created for selective suspend handling
 *
*/

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

//////////////////////////////////////////////////////////////////////////////
//
// SelectiveSuspend.cpp
//
// This file contains the implementation of the class "CSelectiveSuspend"
// which implements the handling of the USB selective suspend feature for
// permanent power saving.
//
//////////////////////////////////////////////////////////////////////////////

#include "precomp.h"

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

//////////////////////////////////////////////////////////////////////////////
//
// Constructor
//
// Parameters:
// None.
//
//////////////////////////////////////////////////////////////////////////////
CSelectiveSuspend::CSelectiveSuspend() {
  InitializeCriticalSection(&m_csLock);
  m_hCloseEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
  m_hResumedEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
  m_hResumedNotificationEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
  m_hSuspendedNotificationEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
  m_pFuncs = NULL;
  m_hDevice = NULL;
  m_dwNumIfaces = 0;
  m_hExecutionThread = NULL;
  m_fExeThreadRunning = FALSE;
  m_dwDelay = 0;
  m_dwNextExpiration = INFINITE;
  m_State = SSR_RESUMED;
#if USE_WA_RESET_ON_RESUME
  m_bmPipeReset = (DWORD)-1;
#endif // USE_WA_RESET_ON_RESUME

#ifdef USE_WA_PAUSE_ON_RESUME
  m_dwPauseOnResumeDelay = 0;
#endif // USE_WA_PAUSE_ON_RESUME

#ifdef USE_WATCHDOG_SUSPEND_DEVICE
  m_pWDSuspend = new CUsbWatchDog( 5000 );
#endif
}

//////////////////////////////////////////////////////////////////////////////
//
// Destructor
//
//////////////////////////////////////////////////////////////////////////////
CSelectiveSuspend::~CSelectiveSuspend() {
#ifdef USE_WATCHDOG_SUSPEND_DEVICE
  if ( m_pWDSuspend != NULL )
      delete m_pWDSuspend;
#endif
  Deinitialize();
  CloseHandle(m_hResumedNotificationEvent);
  CloseHandle(m_hSuspendedNotificationEvent);
  CloseHandle(m_hResumedEvent);
  CloseHandle(m_hCloseEvent);
  DeleteCriticalSection(&m_csLock);
}

//////////////////////////////////////////////////////////////////////////////
//
// Initialize the selective suspend handling.
//
// Parameters:
// Device  : Handle to our USB device.
// Funcs   : Pointer to a USB device interface function table.
// dwIfaces: Number of interfaces of the USB device.
// dwDelay : Delay of inactivity in ms after which the device is supsended.
//           A value of 0 or INFINITE indicates, that selective suspend shall
//           not be used.
//
// Return:
// TRUE on successs, FALSE otherwise.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CSelectiveSuspend::Initialize(USB_HANDLE Device, LPCUSB_FUNCS Funcs, DWORD dwIfaces, DWORD dwDelay) {
  SYNCBLOCK(m_csLock);
  if (!Device) return FALSE;
  if (!Funcs) return FALSE;
  if (!m_hCloseEvent) return FALSE;
  if (!m_hResumedEvent) return FALSE;
  if (!m_hResumedNotificationEvent) return FALSE;
  if (!m_hSuspendedNotificationEvent) return FALSE;
  m_pFuncs = Funcs;
  m_hDevice = Device;
  m_dwNumIfaces = dwIfaces;
  m_dwDelay = dwDelay;
  m_dwNextExpiration = INFINITE;
  m_State = SSR_RESUMED;
  ResetEvent(m_hCloseEvent);
  SetEvent(m_hResumedEvent);
  SetEvent(m_hResumedNotificationEvent);
  ResetEvent(m_hSuspendedNotificationEvent);
  return TRUE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Deinitialize the selective suspend handling.
//
// Parameters:
// None.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CSelectiveSuspend::Deinitialize(void) {
BOOL fRet = TRUE;

  DEBUGMSG(ZONE_USB_SUSPEND|ZONE_USB_RESUME, (TEXT("HcUsb: +CSelectiveSuspend::Deinitialize()\r\n")));
  DEBUGMSG(ZONE_LOCK, (TEXT("HcUsb:  CSelectiveSuspend::Deinitialize() - sync: +CSelectiveSuspend\r\n")));
  SYNCBLOCK(m_csLock);
  DEBUGMSG(ZONE_LOCK, (TEXT("HcUsb:  CSelectiveSuspend::Deinitialize() - sync: -CSelectiveSuspend\r\n")));
  if (m_hCloseEvent) {
    SetEvent(m_hCloseEvent);
    fRet = TRUE;
    if (m_hExecutionThread) {
      DEBUGMSG(ZONE_USB_SUSPEND|ZONE_USB_RESUME, (TEXT("HcUsb:  CSelectiveSuspend::Deinitialize() - wait for exec thread\r\n")));
      CeSetThreadPriority(m_hExecutionThread, 100); // Ensure that thread is able to kick in
      { UNSYNCBLOCK(m_csLock);
        if (WaitForSingleObject(m_hExecutionThread, 5000) != WAIT_OBJECT_0) {
          DEBUGMSG(ZONE_USB_SUSPEND|ZONE_USB_RESUME|ZONE_ERROR, (TEXT("HcUsb:  CSelectiveSuspend::Deinitialize() - ERROR: exec thread not terminated\r\n")));
          fRet = FALSE;
        }
      }
      CloseHandle(m_hExecutionThread);
      m_hExecutionThread = NULL;
    }
  }
  m_pFuncs = NULL;
  m_hDevice = NULL;
  m_dwNumIfaces = 0;

  DEBUGMSG(ZONE_USB_SUSPEND|ZONE_USB_RESUME, (TEXT("HcUsb: -CSelectiveSuspend::Deinitialize(%d)\r\n"), fRet));
  return fRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// This function has to be called to indicate device activity to delay the
// next device suspend. If the device is supended it will be resumed.
//
// Params:
// None
//
// Return:
// TRUE on successs, FALSE otherwise.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CSelectiveSuspend::DeviceAccessed(void) {
BOOL fRet = FALSE;

  DEBUGMSG(ZONE_LOCK, (TEXT("HcUsb:  CSelectiveSuspend::DeviceAccessed() - sync: +CSelectiveSuspend\r\n")));
  SYNCBLOCK(m_csLock);
  DEBUGMSG(ZONE_LOCK, (TEXT("HcUsb:  CSelectiveSuspend::DeviceAccessed() - sync: -CSelectiveSuspend\r\n")));

  if (!IsRunning()) return FALSE;
  if (m_dwDelay == INFINITE) return TRUE;
  ResumeDevice();

  if (m_fExeThreadRunning) {
    // Timer is already running, we need to restart it
    m_dwNextExpiration = GetTickCount() + m_dwDelay;
    fRet = TRUE;
  } else {
    // Close possibly open handle
    if (m_hExecutionThread) {
      CloseHandle(m_hExecutionThread);
    }
    // Start the timer execution thread
    m_hExecutionThread = CreateThread(NULL, 0, ExecutionThread, this, 0, NULL);
    if (m_hExecutionThread) {
      m_fExeThreadRunning = TRUE;
      fRet = TRUE;
    }
    else
    {
      DWORD lastErr = GetLastError();
      DEBUGMSG(ZONE_ERROR, (TEXT("HcUsb:  CSelectiveSuspend::DeviceAccessed() - CreateThread() failed - last error = %d\r\n"), lastErr));
    }
  }
  return fRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// This function waits for the device to resume for the given timeout.
//
// Params:
// dwTimeout: The timeout after which the function returns in any case.
//
// Return:
// TRUE in case of timeout or device is resumed, FALSE in any other case.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CSelectiveSuspend::WaitForResume(DWORD dwTimeout, HANDLE hEvent) {
HANDLE  hEvents[] = { m_hResumedEvent, m_hCloseEvent, hEvent };
DWORD   dwEvents = ( hEvent == NULL ? 2 : 3 );
DWORD   dwWaitRet;

  if (!m_hCloseEvent || !m_hResumedEvent) return FALSE;

  if ( IsResumed() ) {
      return TRUE;
  }

  dwWaitRet = WaitForMultipleObjects(dwEvents, hEvents, FALSE, dwTimeout);
  if ( dwWaitRet == ( WAIT_OBJECT_0 + 1 ) ) {
      DEBUGMSG(ZONE_USB_RESUME, (TEXT("HcUsb: -CSelectiveSuspend::WaitForResume(0) - Close event detected\r\n")));
      return FALSE;
  }
  if ( ( hEvent != NULL ) && ( dwWaitRet == ( WAIT_OBJECT_0 + 2 ) ) ) {
      DEBUGMSG(ZONE_USB_RESUME, (TEXT("HcUsb: -CSelectiveSuspend::WaitForResume(0) - Custom abort event detected\r\n")));
      return FALSE;
  }

  if ((dwWaitRet == WAIT_OBJECT_0) || (dwWaitRet == WAIT_TIMEOUT)) {
      return TRUE;
  }

  DEBUGMSG(ZONE_USB_RESUME, (TEXT("HcUsb: -CSelectiveSuspend::WaitForResume(0) - Wait Error\r\n")));
  return FALSE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Function has to be called to signal that the device is resumed.
//
// Params:
// None
//
// Return:
// TRUE if the resume notification fits to the internal state, FALSE
// otherwise.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CSelectiveSuspend::DeviceResumed(void) {
BOOL fRet = FALSE;

  switch ( m_State ) {
  case SSR_RESUMED:
    // ignore - already resumed
    DEBUGMSG(ZONE_USB_RESUME, (TEXT("HcUsb:  CSelectiveSuspend::DeviceResumed() - already resumed\r\n")));
    break;

  case SSR_RESUMING:
    DEBUGMSG(ZONE_USB_RESUME, (TEXT("HcUsb:  CSelectiveSuspend::DeviceResumed() - resuming\r\n")));
    // => set event to notify SSR thread to resume next interface
    SetEvent(m_hResumedNotificationEvent);
    fRet = TRUE;
    break;

  case SSR_SUSPENDING:
    DEBUGMSG(ZONE_USB_RESUME, (TEXT("HcUsb:  CSelectiveSuspend::DeviceResumed() - abort suspending\r\n")));
    // => set event to notify SSR thread to abort suspending
    m_State = SSR_RESUMED;
    SetEvent(m_hResumedNotificationEvent);
    fRet = TRUE;
    break;

  case SSR_SUSPENDED:
    DEBUGMSG(ZONE_USB_RESUME, (TEXT("HcUsb:  CSelectiveSuspend::DeviceResumed() - remote wakeup\r\n")));
    m_State = SSR_RESUMED;
    // trigger selective susped handling in case there are no transfer after remote wakeup
    DeviceAccessed();
    SetEvent(m_hResumedEvent);
    fRet = TRUE;
    break;

  default:
    DEBUGMSG(ZONE_USB_RESUME|ZONE_ERROR, (TEXT("HcUsb:  CSelectiveSuspend::DeviceResumed() - internal error (unknown state) !!!\r\n")));
    break;
  }

  return fRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Function has to be called to signal that the device is suspended.
//
// Params:
// None
//
// Return:
// TRUE if the suspend notification fits to the internal state, FALSE
// otherwise.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CSelectiveSuspend::DeviceSuspended(void) {
BOOL fRet = FALSE;

  switch ( m_State ) {
  case SSR_SUSPENDED:
    // invalid state: may not happen (SUSPENDED is set when all suspend notification are received)
    DEBUGMSG(ZONE_USB_SUSPEND|ZONE_ERROR, (TEXT("HcUsb:  CSelectiveSuspend::DeviceSuspended() - internal error (wrong state SSR_SUSPENDED) !!!\r\n")));
    break;

  case SSR_SUSPENDING:
    // continue with suspend process
    DEBUGMSG(ZONE_USB_SUSPEND, (TEXT("HcUsb:  CSelectiveSuspend::DeviceSuspended() - interface is suspended\r\n")));
    SetEvent(m_hSuspendedNotificationEvent);
    fRet = TRUE;
    break;

  case SSR_RESUMING:
    // invalid state: may not happen (ResumeDevice() is called while SuspendDevice() is running)
    DEBUGMSG(ZONE_USB_SUSPEND|ZONE_ERROR, (TEXT("HcUsb:  CSelectiveSuspend::DeviceSuspended() - internal error (wrong state SSR_RESUMING) !!!\r\n")));
    break;

  case SSR_RESUMED:
    // may happen if resumed notification is received shortly before awaiting suspended notification is received
    // => ignore it
    DEBUGMSG(ZONE_USB_SUSPEND, (TEXT("HcUsb:  CSelectiveSuspend::DeviceSuspended() - device is already resumed\r\n")));
    break;

  default:
    DEBUGMSG(ZONE_USB_SUSPEND|ZONE_ERROR, (TEXT("HcUsb:  CSelectiveSuspend::DeviceSuspended() - internal error (unknown state) !!!\r\n")));
    break;
  }

  return fRet;
}

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

//////////////////////////////////////////////////////////////////////////////
//
// This function waits for an ongoing suspend/resume transition to finish for
// the given timeout. If the state transition cannot be finished inside the
// given timeout the function tries to tidy up the internal and external
// states properly.
//
// !!! Attention !!!
// It is assumed that the function is called inside a single accessed
// critical section "m_csLock"!
//
// Params:
// dwTimeout: The timeout after which the function returns in any case.
//
// Return:
// TRUE if ongoing transition is finished properly, FALSE in any other case.
//
//////////////////////////////////////////////////////////////////////////////
#if defined(USB_DEVICE_SUSPENDED) && defined(USB_DEVICE_RESUMED)
BOOL CSelectiveSuspend::WaitForResumeTransition(DWORD dwTimeout) {
HANDLE hEvents[] = { m_hResumedNotificationEvent, m_hSuspendedNotificationEvent, m_hCloseEvent };
BOOL fRet = FALSE;

  DEBUGMSG(ZONE_USB_RESUME, (TEXT("HcUsb: +CSelectiveSuspend::WaitForResumeTransition(timeout=%d)\r\n"),dwTimeout));

  if (!m_hCloseEvent || !m_hSuspendedNotificationEvent || !m_hResumedNotificationEvent) {
    DEBUGMSG(ZONE_USB_RESUME | ZONE_ERROR, (TEXT("HcUsb: -CSelectiveSuspend::WaitForResumeTransition(%d) - missing events\r\n"), fRet));
    return FALSE;
  }

  switch ( WaitForMultipleObjects(3, hEvents, FALSE, dwTimeout) ) {
  case WAIT_OBJECT_0:
    // interface is resumed
    DEBUGMSG(ZONE_USB_RESUME, (TEXT("HcUsb:  CSelectiveSuspend::WaitForResumeTransition() - resumed ...\r\n")));
    fRet = TRUE;
    break;

  case WAIT_OBJECT_0 + 1:
    // suspended meanwhile ...
    DEBUGMSG(ZONE_USB_RESUME | ZONE_ERROR, (TEXT("HcUsb:  CSelectiveSuspend::WaitForResumeTransition() - suspended meanwhile ... internal error\r\n")));
    // => invalid state: may not happen (DeviceSuspend() is called while DeviceResume() is running)
    break;

  case WAIT_OBJECT_0 + 2:
    // closed meanwhile ...
    DEBUGMSG(ZONE_USB_RESUME, (TEXT("HcUsb:  CSelectiveSuspend::WaitForResumeTransition() - closed meanwhile ...\r\n")));
    break;

  case WAIT_TIMEOUT:
    DEBUGMSG(ZONE_USB_RESUME, (TEXT("HcUsb:  CSelectiveSuspend::WaitForResumeTransition() - timeout ...\r\n")));
    // We failed to resume the interface => assume that all interfaces are still suspended
    break;

  default:
    // unhandled wait result !!!
    DEBUGMSG(ZONE_USB_RESUME, (TEXT("HcUsb: +CSelectiveSuspend::WaitForResumeTransition() - default !!!\r\n")));
    break;
  }

  ResetEvent( m_hResumedNotificationEvent );

  DEBUGMSG(ZONE_USB_RESUME, (TEXT("HcUsb: -CSelectiveSuspend::WaitForResumeTransition(%d)\r\n"), fRet));
  return fRet;
}

BOOL CSelectiveSuspend::WaitForSuspendTransition(DWORD dwTimeout) {
HANDLE hEvents[] = { m_hSuspendedNotificationEvent, m_hResumedNotificationEvent, m_hCloseEvent };
BOOL fRet = FALSE;

  DEBUGMSG(ZONE_INFO & ZONE_USB_SUSPEND, (TEXT("HcUsb: +CSelectiveSuspend::WaitForSuspendTransition(timeout=%d)\r\n"),dwTimeout));

  if (!m_hCloseEvent || !m_hSuspendedNotificationEvent || !m_hResumedNotificationEvent) {
    DEBUGMSG(ZONE_USB_SUSPEND | ZONE_ERROR, (TEXT("HcUsb: -CSelectiveSuspend::WaitForSuspendTransition(%d) - missing events\r\n"), fRet));
    return fRet;
  }

  switch ( WaitForMultipleObjects(3, hEvents, FALSE, dwTimeout) ) {
  case WAIT_OBJECT_0:
    // interface is suspended
    DEBUGMSG(ZONE_INFO & ZONE_USB_SUSPEND, (TEXT("HcUsb:  CSelectiveSuspend::WaitForSuspendTransition() - suspended ...\r\n")));
    fRet = TRUE;
    break;

  case WAIT_TIMEOUT:
    // failed to wait for state transition
    DEBUGMSG(ZONE_WARN & ZONE_USB_SUSPEND, (TEXT("HcUsb:  CSelectiveSuspend::WaitForSuspendTransition() - timeout ...\r\n")));
    break;

  case WAIT_OBJECT_0 + 1:
    // resumed meanwhile ...
    DEBUGMSG(ZONE_WARN & ZONE_USB_SUSPEND, (TEXT("HcUsb:  CSelectiveSuspend::WaitForSuspendTransition() - resumed meanwhile ...\r\n")));
    break;

  case WAIT_OBJECT_0 + 2:
    // closed meanwhile ...
    DEBUGMSG(ZONE_WARN & ZONE_USB_SUSPEND, (TEXT("HcUsb:  CSelectiveSuspend::WaitForSuspendTransition() - closed meanwhile ...\r\n")));
    break;

  default:
    // unhandled wait result !!!
    DEBUGMSG(ZONE_WARN & ZONE_USB_SUSPEND, (TEXT("HcUsb: +CSelectiveSuspend::WaitForSuspendTransition() - default !!!\r\n")));
    break;
  }

  ResetEvent( m_hSuspendedNotificationEvent );

  DEBUGMSG(ZONE_INFO & ZONE_USB_SUSPEND, (TEXT("HcUsb: -CSelectiveSuspend::WaitForSuspendTransition(%d)\r\n"), fRet));
  return fRet;
}
#endif
//////////////////////////////////////////////////////////////////////////////
//
// Suspend all interfaces of the connected device to get the whole device
// suspended. The function blocks until the suspend has finished.
//
// Params:
// None
//
// Return:
// TRUE on successs, FALSE otherwise.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CSelectiveSuspend::SuspendDevice(void) {
DWORD  i;
BOOL   fRet = TRUE;
SSR_STATE oldState = m_State;

  DEBUGMSG(ZONE_USB_SUSPEND, (TEXT("HcUsb: +CSelectiveSuspend::SuspendDevice()\r\n")));
  if (IsSuspended()) {
    DEBUGMSG(ZONE_USB_SUSPEND, (TEXT("HcUsb: -CSelectiveSuspend::SuspendDevice(1) -> Device is already suspended\r\n")));
    return TRUE;
  }

  ResetEvent(m_hResumedEvent);

#if defined(USB_DEVICE_SUSPENDED) && defined(USB_DEVICE_RESUMED)
  ResetEvent(m_hResumedNotificationEvent);
  ResetEvent(m_hSuspendedNotificationEvent);
  m_State = SSR_SUSPENDING;
#endif // USB_DEVICE_SUSPENDED && USB_DEVICE_RESUMED

#if USE_WA_RESET_ON_RESUME
  m_bmPipeReset = 0;
#endif // USE_WA_RESET_ON_RESUME

  // suspend all interfaces ...
  for (i = 0;
#if defined(USB_DEVICE_SUSPENDED) && defined(USB_DEVICE_RESUMED)
         (m_State == SSR_SUSPENDING) &&
#endif // USB_DEVICE_SUSPENDED && USB_DEVICE_RESUMED
        (i < m_dwNumIfaces); i++) {
    DEBUGMSG(ZONE_USB_HOSTFN, (TEXT("HcUsb: CSelectiveSuspend::SuspendDevice(): +lpSuspendDevice() on interface %d\r\n"), i));
#ifdef USE_WATCHDOG_SUSPEND_DEVICE
    WCHAR wszMessage[128];
    wsprintf( wszMessage, L"lpSuspendDevice() on interface %d", i );
    m_pWDSuspend->start( wszMessage );
#endif
    fRet = m_pFuncs->lpSuspendDevice(m_hDevice, (UCHAR)i);
    if ( fRet == FALSE ) {
        DEBUGMSG(ZONE_USB_HOSTFN_ERROR, (TEXT("HcUsb: lpSuspendDevice() - failed on if %d\r\n"), i));
    }
#ifdef USE_WATCHDOG_SUSPEND_DEVICE
    m_pWDSuspend->stop();
#endif
    DEBUGMSG(ZONE_USB_HOSTFN, (TEXT("HcUsb: CSelectiveSuspend::SuspendDevice(): -lpSuspendDevice(%s) on interface %d\r\n"), fRet?TEXT("TRUE"):TEXT("FALSE"), i));

    if (fRet == FALSE) {
      DEBUGMSG(ZONE_USB_SUSPEND | ZONE_ERROR, (TEXT("HcUsb:  CSelectiveSuspend::SuspendDevice() failed to suspend interface %d\r\n"), i));
      break;
    }
    #if defined(USB_DEVICE_SUSPENDED) && defined(USB_DEVICE_RESUMED)
      if (!WaitForSuspendTransition(TRANSITION_TIMEOUT)) {
        fRet = FALSE;
        DEBUGMSG(ZONE_USB_SUSPEND | ZONE_ERROR, (TEXT("HcUsb:  CSelectiveSuspend::SuspendDevice() - suspend error at IF %d\r\n"), i));
        break;
      }
    #endif
  }
  // check if suspending succeeded ...
  if ( ( fRet == TRUE )
#if defined(USB_DEVICE_SUSPENDED) && defined(USB_DEVICE_RESUMED)
       // ... and the device does not resumed meanwhile ...
       && (m_State == SSR_SUSPENDING)
#endif
     ) {
    m_State = SSR_SUSPENDED;
  } else {
    // ... if resumed meanwhile then resume already suspended interfaces ...
    for (DWORD j = 0; j < i; j++) {
      DEBUGMSG(ZONE_USB_SUSPEND, (TEXT("HcUsb:  CSelectiveSuspend::SuspendDevice() aborted -  resume interface %d\r\n"), j ));
      DEBUGMSG(ZONE_USB_HOSTFN, (TEXT("HcUsb: CSelectiveSuspend::SuspendDevice(): +lpResumeDevice() interface %d\r\n"), j));
      BOOL fRet = m_pFuncs->lpResumeDevice(m_hDevice, (UCHAR)j);
      DEBUGMSG(ZONE_USB_HOSTFN, (TEXT("HcUsb: CSelectiveSuspend::SuspendDevice(): -lpResumeDevice() interface %d\r\n"), j));
      if ( fRet == FALSE ) {
        DEBUGMSG(ZONE_USB_HOSTFN_ERROR, (TEXT("HcUsb: lpResumeDevice() - failed on if %d\r\n"), j));
      }
    }
    m_State = SSR_RESUMED;
    // ... and notify all threads waiting for the resume event
    SetEvent(m_hResumedEvent);
    // .. and restart the selective suspend handling
    DeviceAccessed();
  }

  DEBUGMSG(ZONE_USB_SUSPEND, (TEXT("HcUsb: -CSelectiveSuspend::SuspendDevice(%d)\r\n"), fRet));
  return fRet;
}

#ifdef USE_WA_RESET_ON_RESUME
BOOL CSelectiveSuspend::ResetPipeOnResume( CUsbPipe *pPipe, USB_PIPE pipe, BYTE index, BOOL isDefaultPipe ) {
  BOOL fRet = TRUE;

  SYNCBLOCK(m_csLock);

  if ( pipe == NULL ) {
      SetLastError( ERROR_INVALID_PARAMETER );
      return FALSE;
  }

  if ( index > ( 8 * sizeof( m_bmPipeReset ) -1 ) ) {
      SetLastError( ERROR_INVALID_PARAMETER );
      return FALSE;
  }

  if ( m_pFuncs == NULL ) {
      SetLastError( ERROR_INVALID_ACCESS );
      return FALSE;
  }

  if ( ( m_bmPipeReset & ( 1 << index ) ) == 0 ) {
    DEBUGMSG(ZONE_USB_SUSPEND|ZONE_USB_RESUME, (TEXT("HcUsb:  CSelectiveSuspend::ResetPipeOnResume() - reset pipe %d on resume\r\n"), index));

    // reset pipe
    if ( isDefaultPipe ) {
      DEBUGMSG(ZONE_USB_HOSTFN, (TEXT("HcUsb:  CSelectiveSuspend::ResetPipeOnResume() +lpResetDefaultPipe()\r\n")));
 #ifdef USE_WATCHDOG_RESET_PIPE
      WCHAR wszMessage[128];
      wsprintf( wszMessage, L"lpResetDefaultPipe() on resume on pipe %d", index & 0x7F );
      pPipe->getWDResetPipe()->start(wszMessage);
 #endif
      DEBUGMSG(ZONE_USB_HOSTFN, (TEXT("HcUsb: CSelectiveSuspend::ResetPipeOnResume(): +lpResetDefaultPipe()\r\n")));
      BOOL fRet = m_pFuncs->lpResetDefaultPipe(m_hDevice);
      DEBUGMSG(ZONE_USB_HOSTFN, (TEXT("HcUsb: CSelectiveSuspend::ResetPipeOnResume(): -lpResetDefaultPipe()\r\n")));
      if ( fRet == FALSE ) {
        DEBUGMSG(ZONE_USB_HOSTFN_ERROR, (TEXT("HcUsb: lpResetDefaultPipe() - failed\r\n")));
      }
#ifdef USE_WATCHDOG_RESET_PIPE
      pPipe->getWDResetPipe()->stop();
#endif
      DEBUGMSG(ZONE_USB_HOSTFN, (TEXT("HcUsb:  CSelectiveSuspend::ResetPipeOnResume() -lpResetDefaultPipe(%d)\r\n"),fRet));
      if (!fRet) {
          DEBUGMSG(ZONE_USB_SUSPEND|ZONE_USB_RESUME|ZONE_ERROR, (TEXT("HcUsb:  CSelectiveSuspend::ResetPipeOnResume(FALSE) - resetting default pipe failed\r\n")));
#ifdef WA_RESUME_ON_UNEXPECTED_HOST_WAKEUPS
          // 1st suspend then resume to not mess up internal states
          return SuspendAndResumeOnResetPipeOnResumeFailed();
#else
          return FALSE;
#endif
      }
    } else {
      DEBUGMSG(ZONE_USB_HOSTFN, (TEXT("HcUsb:  CSelectiveSuspend::ResetPipeOnResume() +lpResetPipe() on pipe %d\r\n"), index));
 #ifdef USE_WATCHDOG_RESET_PIPE
      WCHAR wszMessage[128];
      wsprintf( wszMessage, L"lpResetPipe() on resume on pipe %d", index & 0x7F );
    pPipe->getWDResetPipe()->start(wszMessage);
 #endif
    BOOL fRet = m_pFuncs->lpResetPipe(pipe);
    DEBUGMSG(ZONE_USB_HOSTFN, (TEXT("HcUsb:  CSelectiveSuspend::ResetPipeOnResume() -lpResetPipe() on pipe %d\r\n"), index));
    if ( fRet == FALSE ) {
      DEBUGMSG(ZONE_USB_HOSTFN_ERROR, (TEXT("HcUsb: lpResetPipe() - failed\r\n")));
    }
 #ifdef USE_WATCHDOG_RESET_PIPE
    pPipe->getWDResetPipe()->stop();
 #endif
      DEBUGMSG(ZONE_USB_HOSTFN, (TEXT("HcUsb:  CSelectiveSuspend::ResetPipeOnResume() -lpResetPipe(%d) on pipe %d\r\n"), fRet, index));
      if (!fRet)  {
          DEBUGMSG(ZONE_USB_SUSPEND|ZONE_USB_RESUME|ZONE_ERROR, (TEXT("HcUsb:  CSelectiveSuspend::ResetPipeOnResume(FALSE) - resetting pipe %d failed\r\n"), index));
          return FALSE;
      }
    }

    if ( fRet ) {
      DEBUGMSG(ZONE_USB_SUSPEND|ZONE_USB_RESUME, (TEXT("HcUsb:  CSelectiveSuspend::ResetPipeOnResume(TRUE) - pipe %d resetted on resume\r\n"), index));
      m_bmPipeReset |= ( 1 << index );
      DeviceAccessed();
      return TRUE;
    }
  }

  return fRet;

}
#endif // USE_WA_RESET_ON_RESUME

//////////////////////////////////////////////////////////////////////////////
//
// Resume all interfaces of the connected device. The function blocks until
// the resume has finished.
//
// Params:
// None
//
// Return:
// TRUE on successs, FALSE otherwise.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CSelectiveSuspend::ResumeDevice(void) {
BOOL   fRet = FALSE;
BOOL   fRes = FALSE;
SSR_STATE oldState = m_State;

  if (IsResumed()) {
      return TRUE;
  }

  DEBUGMSG(ZONE_USB_RESUME, (TEXT("HcUsb: +CSelectiveSuspend::ResumeDevice()\r\n")));

  if ( m_State != SSR_SUSPENDED ) {
    DEBUGMSG(ZONE_USB_RESUME | ZONE_ERROR, (TEXT("HcUsb: -CSelectiveSuspend::ResumeDevice() - device is NOT suspended\r\n")));
    return FALSE;
  }

#if defined(USB_DEVICE_SUSPENDED) && defined(USB_DEVICE_RESUMED)
  ResetEvent(m_hResumedNotificationEvent);
  ResetEvent(m_hSuspendedNotificationEvent);
  m_State = SSR_RESUMING;
#endif

  DEBUGMSG(ZONE_USB_HOSTFN, (TEXT("HcUsb:  CSelectiveSuspend::ResumeDevice() +lpResumeDevice()\r\n")));
  fRet = m_pFuncs->lpResumeDevice(m_hDevice, (UCHAR)0);
  DEBUGMSG(ZONE_USB_HOSTFN, (TEXT("HcUsb:  CSelectiveSuspend::ResumeDevice() -lpResumeDevice()\r\n")));
  if ( fRet == FALSE ) {
    DEBUGMSG(ZONE_USB_HOSTFN_ERROR, (TEXT("HcUsb: lpResumeDevice() - failed\r\n")));
  }
  DEBUGMSG(ZONE_USB_HOSTFN, (TEXT("HcUsb:  CSelectiveSuspend::ResumeDevice() -lpResumeDevice(%d)\r\n"), fRet));

  if ( fRet == FALSE ) {
    DEBUGMSG(ZONE_USB_RESUME | ZONE_ERROR, (TEXT("HcUsb:  CSelectiveSuspend::ResumeDevice() failed to resume device\r\n")));
  } else {
#if defined(USB_DEVICE_SUSPENDED) && defined(USB_DEVICE_RESUMED)
    if (!WaitForResumeTransition(TRANSITION_TIMEOUT)) {
      fRet = FALSE;
      DEBUGMSG(ZONE_USB_RESUME | ZONE_ERROR, (TEXT("HcUsb:  CSelectiveSuspend::ResumeDevice() failed to wait for resume to finish\r\n")));
    } else {
      m_State = SSR_RESUMED;
      SetEvent(m_hResumedEvent);
    }
#else // USB_DEVICE_SUSPENDED && USB_DEVICE_RESUMED
    m_State = SSR_RESUMED;
    SetEvent(m_hResumedEvent);
#endif // USB_DEVICE_SUSPENDED && USB_DEVICE_RESUMED
  }

  if ( fRet == FALSE ) {
    // failed to resume the device; assume the device is still suspended
    m_State = SSR_SUSPENDED;
  }
#ifdef USE_WA_PAUSE_ON_RESUME
  else
  {
      DEBUGMSG(ZONE_USB_RESUME, (TEXT("HcUsb:  CSelectiveSuspend::ResumeDevice() - delay next transactions for %d ms\r\n"), m_dwPauseOnResumeDelay));
      Sleep(m_dwPauseOnResumeDelay);
  }
#endif

  DEBUGMSG(ZONE_USB_RESUME, (TEXT("HcUsb: -CSelectiveSuspend::ResumeDevice(%d)\r\n"), fRet));
  return fRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Execution thread for the running timer.
//
// Parameters:
// lpParameter: "This" pointer to our class instance
//
// Return:
// 0 on success.
//
//////////////////////////////////////////////////////////////////////////////
DWORD WINAPI CSelectiveSuspend::ExecutionThread(LPVOID lpParameter) {
CSelectiveSuspend*  pInstance = (CSelectiveSuspend*)lpParameter;
DWORD               dwDelay;
DWORD               dwTickCount;

  DEBUGMSG(ZONE_INFO & ZONE_USB_SUSPEND, (TEXT("HcUsb: +CSelectiveSuspend::ExecutionThread()\r\n")));

  EnterCriticalSection(&(pInstance->m_csLock));
  dwDelay = pInstance->m_dwDelay;
  LeaveCriticalSection(&(pInstance->m_csLock));

  // Loop until timer is stopped or expired
  while (WaitForSingleObject(pInstance->m_hCloseEvent, dwDelay) != WAIT_OBJECT_0) {
    SYNCBLOCK(pInstance->m_csLock);

    if (pInstance->m_dwNextExpiration == INFINITE) {
      // Timer has expired without having been restarted
      pInstance->m_fExeThreadRunning = FALSE;
      if (pInstance->IsRunning()) {
        pInstance->SuspendDevice();
      }
      DEBUGMSG(ZONE_INFO & ZONE_USB_SUSPEND, (TEXT("HcUsb: -CSelectiveSuspend::ExecutionThread(0)\r\n")));
      return 0;
    } else {
      // Timer has been restarted meanwhile
      dwTickCount = GetTickCount();
      if (dwTickCount >= pInstance->m_dwNextExpiration) {
        // The timer has expired nevertheless
        pInstance->m_fExeThreadRunning = FALSE;
        pInstance->m_dwNextExpiration = INFINITE;
        if (pInstance->IsRunning()) {
          pInstance->SuspendDevice();
        }
        DEBUGMSG(ZONE_INFO & ZONE_USB_SUSPEND, (TEXT("HcUsb: -CSelectiveSuspend::ExecutionThread(0)\r\n")));
        return 0;
      } else {
        // We need to restart the timer
        dwDelay = pInstance->m_dwNextExpiration - dwTickCount;
        pInstance->m_dwNextExpiration = INFINITE;
      }
    }
  }

   pInstance->m_fExeThreadRunning = FALSE;

  DEBUGMSG(ZONE_INFO & ZONE_USB_SUSPEND, (TEXT("HcUsb: -CSelectiveSuspend::ExecutionThread(-1)\r\n")));
  return -1;
}

#ifdef WA_RESUME_ON_UNEXPECTED_HOST_WAKEUPS
BOOL CSelectiveSuspend::SuspendAndResumeOnResetPipeOnResumeFailed(void) {
BOOL fRet = FALSE;

  DEBUGMSG(ZONE_USB, (TEXT("HcUsb: +CSelectiveSuspend::SuspendAndResumeOnResetPipeOnResumeFailed()\r\n")));
  if (SuspendDevice() == TRUE) {
    fRet = ResumeDevice();
  }
  DeviceAccessed();
  DEBUGMSG(ZONE_USB, (TEXT("HcUsb: -CSelectiveSuspend::SuspendAndResumeOnResetPipeOnResumeFailed(%d)\r\n"), fRet));
  return fRet;
}
#endif // WA_RESUME_ON_UNEXPECTED_HOST_WAKEUPS

