/*****************************************************************************
 * Copyright (C) Cinterion Wireless Modules GmbH
 *****************************************************************************
 * Project: Quinn-CE-USB-Driver
 *****************************************************************************
 * Author : Siemens AG Berlin COM WM RD SW
 *****************************************************************************
 * $Workfile: O:/mwohler_z_ms-d/z_ms-d/src/working/Quinn/HcUsbDrv/Util.cpp $
 * $Revision: /main/ms-d/22 $
 * $Date: 2013-11-01 11:48:42 $
 * $Author: mwohler $
 *****************************************************************************
 * Target:
 *  Microsoft Windows CE 5.0
 *****************************************************************************
 * Description:
 *  UMTS-Module USB-Driver: Useful classes and utility function
 *  implementations
 *****************************************************************************
 *
 * $Log: O:/mwohler_z_ms-d/z_ms-d/src/working/Quinn/HcUsbDrv/Util.cpp$ 
 * 
 * /main/ms-d/22   2013-11-01 11:48:42   mwohler 
 * Fixed problem with lost WaitCommEvent() calls in case of very fast consecutive events. 
 * 
 * /main/ms-d/21   2013-06-19 17:37:55   thinze 
 * improved debug messages 
 * 
 * /main/ms-d/20   2009-09-04 12:53:45   ctna0450 
 * Tidy up CommEvent handling properly without deadlock and exception
 *
 * /main/ms-d/19   2009-09-02 10:23:27   ctna0450
 * Fixed deadlock in CommEvent handling
 *
 * /main/ms-d/18   2009-02-27 15:15:01   ctna0450
 * Added very useful functionality in CRingBuffer for read/write synchronization
 *
 * /main/ms-d/17   2009-02-26 14:06:07   ctna0450
 * Do not leave critical section twice
 *
 * /main/ms-d/16   2009-02-18 11:49:54   ctna0256
 * Set copyright text
 *
 * /main/ms-d/15   2009-02-02 10:52:56   ctna0152
 * bugfix: call ReleaseWaitEvents() instead of SetWaitEvent(0) on Close
 *
 * /main/ms-d/14   2009-01-19 14:26:20   ctna0450
 * Fixed possible exception during closing port
 *
 * /main/ms-d/13   2009-01-14 10:47:43   ctna0450
 * Make virtual port instancable to properly support handling of consecutive WaitCommEvent() calls
 *
 * /main/ms-d/12   2008-08-20 11:39:38   ctna0450
 * Prepared for new disclaimer handling
 *
 * /main/ms-d/11   2008-06-19 14:22:14   ht37010
 * added workaround for missing RXCHAR event
 *
 * /main/ms-d/10   2008-03-03 11:10:12   ht37010
 * bugfix in Resize when copying data into new buffer
 *
 * /main/ms-d/9   2007-04-12 13:36:34   ht37010
 * new function ReleaseWaitEvents() as workaround for reset on resume
 *
 * /main/ms-d/8   2006-11-03 17:07:04   wm36264
 * Continued to prepare selective suspend handling
 *
 * /main/ms-d/7   2006-11-01 18:31:15   wm36264
 * Implemented simple timer class for future selective suspend
 *
 * /main/ms-d/6   2006-10-06 18:00:24   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:14   wm36264
 * Consider latest findings about QMI and NDIS handling
 *
 * /main/ms-d/4   2006-09-27 17:07:06   wm36264
 * Switched to central version and trace handling
 *
 * /main/ms-d/3   2006-09-22 14:33:15   wm36264
 * Cosmetics mostly
 *
 * /main/ms-d/2   2006-09-21 17:05:32   wm36264
 * Prepared access to QMI interface
 *
 * /main/ms-d/1   2006-09-18 15:31:54   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
//
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//
// Util.cpp
//
// This file contains the implementation of the helper class "CWaitCommEvent"
// used inside the class "CVirtualPort" to handle calls of "WaitCommEvent()"
// and "SetCommMask()" and the helper class "CRingBuffer".
//
//////////////////////////////////////////////////////////////////////////////

#include "precomp.h"

//////////////////////////////////////////////////////////////////////////////
//
// CWaitCommEvent - Public interface
//
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//
// Constructor
//
// Parameters:
// None.
//
//////////////////////////////////////////////////////////////////////////////
CWaitCommEvent::CWaitCommEvent() {
  InitializeCriticalSection(&m_csLockDestruct);
  InitializeCriticalSection(&m_csLock);
  m_dwWaitMask = 0;
  m_dwEventListSize = INCREASE_LIST_DIFF;
  m_dwPendingEvent = 0;
  m_dwWaitForEventCalls = 0;
  m_pEventList = new WAIT_EVENT_INFO[m_dwEventListSize];
  memset(m_pEventList, 0, sizeof(WAIT_EVENT_INFO) * m_dwEventListSize);
}

//////////////////////////////////////////////////////////////////////////////
//
// Destructor
//
//////////////////////////////////////////////////////////////////////////////
CWaitCommEvent::~CWaitCommEvent() {
  EnterCriticalSection(&m_csLockDestruct);
  if (m_pEventList) {
    // Release all waiting events
    ReleaseWaitEvents();
    // Wait for all WaitForEvent() calls to finish
    EnterCriticalSection(&m_csLock);
    DWORD dwOpenWaitCalls = m_dwWaitForEventCalls;
    LeaveCriticalSection(&m_csLock);
    for (DWORD i = 0; (i < 500) && dwOpenWaitCalls; i++) {
      Sleep(10);
      EnterCriticalSection(&m_csLock);
      dwOpenWaitCalls = m_dwWaitForEventCalls;
      LeaveCriticalSection(&m_csLock);
    }
    // Remove event list
    EnterCriticalSection(&m_csLock);
    delete [] m_pEventList;
    m_pEventList = NULL;
    LeaveCriticalSection(&m_csLock);
  }
  LeaveCriticalSection(&m_csLockDestruct);
  DeleteCriticalSection(&m_csLock);
  DeleteCriticalSection(&m_csLockDestruct);
}

//////////////////////////////////////////////////////////////////////////////
//
// Wait for a specific event.
//
// Parameters:
// pdwEvent: Here the occured event is passed back, it is a return value.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CWaitCommEvent::WaitForEvent(PDWORD pdwEvent) {
HANDLE  hEvent;
DWORD   dwWaitRet;
DWORD   i;

  hEvent = AddWaitEvent();
  if (!hEvent) return FALSE;
  dwWaitRet = WaitForSingleObject(hEvent, INFINITE);
  SYNCBLOCK(m_csLock);
  for (i = 0; i < m_dwEventListSize; i++) {
    if (m_pEventList[i].hEvent == hEvent) {
      if (m_pEventList[i].fIsValid) {
        *pdwEvent = m_pEventList[i].dwEvent;
      } else {
        dwWaitRet = WAIT_FAILED;
      }
      CloseHandle(m_pEventList[i].hEvent);
      m_pEventList[i].hEvent = NULL;
    }
  }
  m_dwWaitForEventCalls--;
  return (dwWaitRet == WAIT_OBJECT_0) ? TRUE : FALSE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Set a specific event and release all corresponding wating "WaitForEvent()"
// calls. If zero is passed as the event all waiting "WaitForEvent()" calls
// are released.
//
// Parameters:
// dwEvent: The event to set.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CWaitCommEvent::SetWaitEvent(DWORD dwEvent) {
DWORD  i;
BOOL   fNotified = FALSE;

  SYNCBLOCK(m_csLock);
  if (!m_pEventList) {
    DEBUGMSG(ZONE_PORT_EVENTS | ZONE_ERROR, (TEXT("HcUsb: CWaitCommEvent::SetWaitEvent(FALSE) - no event list\r\n")));
    return FALSE;
  }

  if ((dwEvent != 0) && ((m_dwWaitMask & dwEvent) == 0)) {
    // there is an event, but is not set in the mask
    DEBUGMSG(ZONE_PORT_EVENTS, (TEXT("HcUsb: CWaitCommEvent::SetWaitEvent() - event not set in mask [this=%p,evt=0x%02X,mask=0x%02X]\r\n"), this, dwEvent, m_dwWaitMask));
    return TRUE;
  }

  // set pending events (will be cleared below if there is a process waiting on
  // the mask, otherwise these events are checked if a process starts to wait
  // on the mask)
  m_dwPendingEvent |= dwEvent;

  // Release all waiting events
  for (i = 0; i < m_dwEventListSize; i++) {
    if (m_pEventList[i].hEvent && ((m_pEventList[i].dwMask & m_dwPendingEvent) || (!dwEvent))) {
      m_pEventList[i].dwEvent |= m_dwPendingEvent;
      m_pEventList[i].fIsValid = TRUE;
      DEBUGMSG(ZONE_PORT_EVENTS, (TEXT("HcUsb: CWaitCommEvent::SetWaitEvent() - set event [this=%p,evt=0x%02X,mask=0x%02X]\r\n"), this, m_dwPendingEvent, m_dwWaitMask));
      fNotified = TRUE;
      SetEvent(m_pEventList[i].hEvent);
    }
  }

  if (fNotified == TRUE) {
    // clear all pending events since there was at least one waiting process notified
    m_dwPendingEvent = 0;
  }
  return TRUE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Release all waiting comm events without resetting the wait mask.
//
// Parameters:
// None.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CWaitCommEvent::ReleaseWaitEvents(void) {
  SYNCBLOCK(m_csLock);
  if (!m_pEventList) return FALSE;
  // Release all waiting events
  for (DWORD i = 0; i < m_dwEventListSize; i++) {
    if (m_pEventList[i].hEvent) {
      m_pEventList[i].fIsValid = FALSE;
      SetEvent(m_pEventList[i].hEvent);
    }
  }
  return TRUE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Set the new wait comm mask.
//
// Parameters:
// dwMask: The new mask for WaitCommEvent() calls.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CWaitCommEvent::SetWaitMask(DWORD dwMask) {
  SYNCBLOCK(m_csLock);
  if (!m_pEventList) {
    DEBUGMSG(ZONE_PORT_EVENTS | ZONE_ERROR, (TEXT("HcUsb: CWaitCommEvent::SetWaitMask(FALSE) - no event list\r\n")));
    return FALSE;
  }
  if (dwMask) {
    if ((dwMask | m_dwWaitMask) != m_dwWaitMask) {
      // From Windows Mobile 6 Documentation: IOCTL_SERIAL_WAIT_ON_MASK
      // If a process attempts to change the device handle's event mask by using
      // the IOCTL_SERIAL_SET_WAIT_MASK call, the driver should return immediately
      // with (DWORD)0 as the returned event mask.
      m_dwWaitMask |= dwMask;
      SetWaitEvent(0);
    }
  } else {
    m_dwWaitMask = 0;
    SetWaitEvent(0);
  }
  return TRUE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Get the current wait comm mask.
//
// Parameters:
// pdwMask: Return value for the current  mask for WaitCommEvent() calls.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CWaitCommEvent::GetWaitMask(PDWORD pdwMask) {
  SYNCBLOCK(m_csLock);
  if (!m_pEventList) return FALSE;
  *pdwMask = m_dwWaitMask;
  return TRUE;
}

//////////////////////////////////////////////////////////////////////////////
//
// CWaitCommEvent - Private implementation
//
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//
// Add a wait event to the internal array. If necessary the array size is
// increased.
//
// Parameters:
// None.
//
// Return:
// The handle of the created event, NULL indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
HANDLE CWaitCommEvent::AddWaitEvent() {
DWORD  i;

  SYNCBLOCK(m_csLock);
  SYNCBLOCK2(m_csLockDestruct);

  if (!m_pEventList) return NULL;

  // Find free place in event array
  for (i = 0; i < m_dwEventListSize; i++) {
    if (!m_pEventList[i].hEvent) {
      break;
    }
  }

  // If no free place found increase size of event list
  if (i == m_dwEventListSize) {
    PWAIT_EVENT_INFO pNewEventList = new WAIT_EVENT_INFO[m_dwEventListSize + INCREASE_LIST_DIFF];
    if (!pNewEventList) {
      return NULL;
    }
    memset(pNewEventList, 0, sizeof(WAIT_EVENT_INFO) * (m_dwEventListSize + INCREASE_LIST_DIFF));
    memcpy(pNewEventList, m_pEventList, sizeof(WAIT_EVENT_INFO) * m_dwEventListSize);
    delete [] m_pEventList;
    m_pEventList = pNewEventList;
    m_dwEventListSize += INCREASE_LIST_DIFF;
  }

  // Add a wait event to the array
  m_pEventList[i].hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  m_pEventList[i].dwMask = m_dwWaitMask;
  m_pEventList[i].dwEvent = 0;
  m_pEventList[i].fIsValid = TRUE;

  // And release the new wait again if we've received a fitting event bofore without wait
  if (m_dwPendingEvent) {
    SetWaitEvent(m_dwPendingEvent);
  }

  m_dwWaitForEventCalls++;

  return m_pEventList[i].hEvent;
}

//****************************************************************************

//////////////////////////////////////////////////////////////////////////////
//
// CWaitCommInstance - Public interface
//
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//
// Constructor
//
// Parameters:
// None.
//
//////////////////////////////////////////////////////////////////////////////
CWaitCommInstance::CWaitCommInstance() {
  InitializeCriticalSection(&m_csLock);
  m_dwInstanceListSize = INCREASE_LIST_DIFF;
  m_pInstanceList = new WAIT_INSTANCE_INFO[m_dwInstanceListSize];
  memset(m_pInstanceList, 0, sizeof(WAIT_INSTANCE_INFO) * m_dwInstanceListSize);
}

//////////////////////////////////////////////////////////////////////////////
//
// Destructor
//
//////////////////////////////////////////////////////////////////////////////
CWaitCommInstance::~CWaitCommInstance() {
  if (m_pInstanceList) {
    SYNCBLOCK(m_csLock);
    RemoveInstance(0);
    delete [] m_pInstanceList;
  }
  DeleteCriticalSection(&m_csLock);
}

//////////////////////////////////////////////////////////////////////////////
//
// Add an instance to the internal instance list.
//
// Parameters:
// pInstance: The instance to be added.
//
// Return:
// None.
//
//////////////////////////////////////////////////////////////////////////////
void CWaitCommInstance::AddInstance(LPVOID pInstance) {
  SYNCBLOCK(m_csLock);
  if (m_pInstanceList && pInstance) {
    DWORD i;

    // Find free place in instance array
    for (i = 0; i < m_dwInstanceListSize; i++) {
      if (!m_pInstanceList[i].pInstance) {
        break;
      }
    }

    // If no free place found increase size of instance list
    if (i == m_dwInstanceListSize) {
      PWAIT_INSTANCE_INFO pNewInstanceList = new WAIT_INSTANCE_INFO[m_dwInstanceListSize + INCREASE_LIST_DIFF];
      if (!pNewInstanceList) return;
      memset(pNewInstanceList, 0, sizeof(WAIT_INSTANCE_INFO) * (m_dwInstanceListSize + INCREASE_LIST_DIFF));
      memcpy(pNewInstanceList, m_pInstanceList, sizeof(WAIT_INSTANCE_INFO) * m_dwInstanceListSize);
      delete [] m_pInstanceList;
      m_pInstanceList = pNewInstanceList;
      m_dwInstanceListSize += INCREASE_LIST_DIFF;
    }

    // Add new instance to the array
    m_pInstanceList[i].pWaitCommEvent = new CWaitCommEvent();
    if (!m_pInstanceList[i].pWaitCommEvent) return;
    m_pInstanceList[i].pInstance = pInstance;
  }
}

//////////////////////////////////////////////////////////////////////////////
//
// Remove an instance from the internal instance list.
//
// Parameters:
// pInstance: The instance to be removed (0 for all instances).
//
// Return:
// None.
//
//////////////////////////////////////////////////////////////////////////////
void CWaitCommInstance::RemoveInstance(LPVOID pInstance) {
  SYNCBLOCK(m_csLock);
  if (m_pInstanceList) {
    for (DWORD i = 0; i < m_dwInstanceListSize; i++) {
      if ((m_pInstanceList[i].pInstance == pInstance) || !pInstance) {
        m_pInstanceList[i].pInstance = 0;
        if (m_pInstanceList[i].pWaitCommEvent) {
          delete m_pInstanceList[i].pWaitCommEvent;
          m_pInstanceList[i].pWaitCommEvent = 0;
        }
      }
    }
  }
}

//////////////////////////////////////////////////////////////////////////////
//
// Release all waiting comm events without resetting the wait mask.
//
// Parameters:
// pInstance: The instance of the call (0 for all instances).
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CWaitCommInstance::ReleaseWaitEvents(LPVOID pInstance) {
BOOL fRet = TRUE;

  SYNCBLOCK(m_csLock);
  if (m_pInstanceList) {
    for (DWORD i = 0; i < m_dwInstanceListSize; i++) {
      if ((m_pInstanceList[i].pInstance == pInstance) || !pInstance) {
        if (m_pInstanceList[i].pWaitCommEvent) {
          if (!m_pInstanceList[i].pWaitCommEvent->ReleaseWaitEvents()) {
            fRet = FALSE;
          }
        }
      }
    }
  }
  return fRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Set a specific event and release all corresponding wating "WaitForEvent()"
// calls. If zero is passed as the event all waiting "WaitForEvent()" calls
// are released.
//
// Parameters:
// dwEvent: The event to set.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CWaitCommInstance::SetWaitEvent(DWORD dwEvent) {
BOOL fRet = TRUE;

  DEBUGMSG(ZONE_PORT_EVENTS, (TEXT("HcUsb: +CWaitCommInstance::SetWaitEvent(%d)\r\n"), dwEvent));

  SYNCBLOCK(m_csLock);
  if (!m_pInstanceList) {
    DEBUGMSG(ZONE_PORT_EVENTS | ZONE_ERROR, (TEXT("HcUsb: -CWaitCommInstance::SetWaitEvent(FALSE) - no instance list\r\n")));
    return FALSE;
  }
  for (DWORD i = 0; i < m_dwInstanceListSize; i++) {
    if (m_pInstanceList[i].pWaitCommEvent) {
      if (!m_pInstanceList[i].pWaitCommEvent->SetWaitEvent(dwEvent)) {
        fRet = FALSE;
      }
    }
  }
  DEBUGMSG(ZONE_PORT_EVENTS, (TEXT("HcUsb: -CWaitCommInstance::SetWaitEvent(%d)\r\n"), fRet));
  return fRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Wait for a specific event.
//
// Parameters:
// pInstance: The instance of the call.
// pdwEvent: Here the occured event is passed back, it is a return value.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CWaitCommInstance::WaitForEvent(LPVOID pInstance, PDWORD pdwEvent) {
  EnterCriticalSection(&m_csLock);
  if (m_pInstanceList && pInstance) {
    for (DWORD i = 0; i < m_dwInstanceListSize; i++) {
      if (m_pInstanceList[i].pInstance == pInstance) {
        if (m_pInstanceList[i].pWaitCommEvent) {
          LeaveCriticalSection(&m_csLock);
          return m_pInstanceList[i].pWaitCommEvent->WaitForEvent(pdwEvent);
        }
      }
    }
  }
  LeaveCriticalSection(&m_csLock);
  return FALSE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Set the new wait comm mask.
//
// Parameters:
// pInstance: The instance of the call (0 for all instances).
// dwMask: The new mask for WaitCommEvent() calls.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CWaitCommInstance::SetWaitMask(LPVOID pInstance, DWORD dwMask) {
BOOL fRet = TRUE;

  SYNCBLOCK(m_csLock);
  if (!m_pInstanceList) return FALSE;
  for (DWORD i = 0; i < m_dwInstanceListSize; i++) {
    if ((m_pInstanceList[i].pInstance == pInstance) || !pInstance) {
      if (m_pInstanceList[i].pWaitCommEvent) {
        if (!m_pInstanceList[i].pWaitCommEvent->SetWaitMask(dwMask)) {
          fRet = FALSE;
        }
      }
    }
  }
  return fRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Get the current wait comm mask.
//
// Parameters:
// pInstance: The instance of the call.
// pdwMask: Return value for the current  mask for WaitCommEvent() calls.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CWaitCommInstance::GetWaitMask(LPVOID pInstance, PDWORD pdwMask) {
  SYNCBLOCK(m_csLock);
  if (!m_pInstanceList || !pInstance) return FALSE;
  for (DWORD i = 0; i < m_dwInstanceListSize; i++) {
    if (m_pInstanceList[i].pInstance == pInstance) {
      return m_pInstanceList[i].pWaitCommEvent->GetWaitMask(pdwMask);
    }
  }
  return FALSE;
}

//****************************************************************************

//////////////////////////////////////////////////////////////////////////////
//
// CRingBuffer - Public interface
//
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//
// Constructor
//
// Parameters:
// None.
//
//////////////////////////////////////////////////////////////////////////////
CRingBuffer::CRingBuffer() {
  InitializeCriticalSection(&m_csLock);
  m_hFreeBytes = CreateEvent(NULL, TRUE, TRUE, NULL);
  m_hUsedBytes = CreateEvent(NULL, TRUE, TRUE, NULL);
  m_dwSize = 0;
  m_dwLevel = 0;
  m_pBase = NULL;
  m_pStart = NULL;
  m_pEnd = NULL;
}

//////////////////////////////////////////////////////////////////////////////
//
// Destructor
//
//////////////////////////////////////////////////////////////////////////////
CRingBuffer::~CRingBuffer() {
  Resize(0);
  CloseHandle(m_hFreeBytes);
  CloseHandle(m_hUsedBytes);
  DeleteCriticalSection(&m_csLock);
}

//////////////////////////////////////////////////////////////////////////////
//
// Resize the ring buffer.
//
// Parameters:
// dwSize: The new ring buffer size.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CRingBuffer::Resize(DWORD dwSize) {
PBYTE  pOldBase;
PBYTE  pOldStart;
DWORD  dwOldSize;
DWORD  dwOldLevel;
DWORD dwDataToWrite;

  SYNCBLOCK(m_csLock);
  if (dwSize != m_dwSize) {
    if (dwSize) {
      // We can only make buffer smaller if it has enough room for existing data
      if (m_dwLevel > dwSize) {
        return FALSE;
      }

      // Create new read buffer
      pOldBase = m_pBase;
      m_pBase = new BYTE[dwSize];
      if (!m_pBase) {
        m_pBase = pOldBase;
        return FALSE;
      }

      memset( m_pBase, 0, dwSize * sizeof(BYTE) );

      dwOldSize = m_dwSize;
      dwOldLevel = m_dwLevel;
      pOldStart = m_pStart;
      m_dwSize = dwSize;
      m_dwLevel = 0;
      m_pStart = m_pBase;
      m_pEnd = m_pBase;

      // Copy existing data from upper part of ring buffer
      dwDataToWrite = min( dwOldLevel, (DWORD)((pOldBase + dwOldSize) - pOldStart) );
      Write(pOldStart, dwDataToWrite);

      // Copy existing data from lower part of ring buffer
      dwDataToWrite = dwOldLevel - dwDataToWrite;
      if ( dwDataToWrite != 0 ) {
          Write(pOldBase, dwDataToWrite);
      }

      // Finally delete the buffer
      if ( pOldBase != NULL ) {
        delete [] pOldBase;
      }

      // Update internal events
      if (m_dwLevel == m_dwSize) {
        ResetEvent(m_hFreeBytes);
      } else {
        SetEvent(m_hFreeBytes);
      }
      if (m_dwLevel) {
        SetEvent(m_hUsedBytes);
      } else {
        ResetEvent(m_hUsedBytes);
      }
    } else {
      // Delete the ring buffer
      if (m_pBase) {
        delete [] m_pBase;
        m_dwSize = 0;
        m_dwLevel = 0;
        m_pBase = NULL;
        m_pStart = NULL;
        m_pEnd = NULL;
        SetEvent(m_hFreeBytes);
        SetEvent(m_hUsedBytes);
      }
    }
  }

  return TRUE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Empty the ring buffer.
//
// Parameters:
// None.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CRingBuffer::Empty(void) {
  SYNCBLOCK(m_csLock);
  if (!m_pBase) return FALSE;
  m_dwLevel = 0;
  m_pStart = m_pBase;
  m_pEnd = m_pBase;
  SetEvent(m_hFreeBytes);
  ResetEvent(m_hUsedBytes);
  return TRUE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Write data into the ring buffer.
//
// Parameters:
// pData: The buffer with the data to write.
// dwLen: The number of bytes to write.
//
// Return:
// The number of actually written bytes which can be less than the length
// passed in dwLen if the read buffer is full. In case of an error -1 is
// returned.
//
//////////////////////////////////////////////////////////////////////////////
DWORD CRingBuffer::Write(PBYTE pData, DWORD dwLen) {
DWORD dwDataToWrite;
DWORD dwDataWritten = 0;

  SYNCBLOCK(m_csLock);

  if (!m_pBase) return (DWORD)-1;

  // Adjust number of bytes to write to free buffer size
  dwLen = min(dwLen, (DWORD)(m_dwSize - m_dwLevel));

  if (dwLen) {
    // Copy data in upper part of ring buffer
    dwDataToWrite = min(dwLen, (DWORD)((m_pBase + m_dwSize) - m_pEnd));
    memcpy(m_pEnd, pData, dwDataToWrite);
    dwDataWritten = dwDataToWrite;
    m_pEnd += dwDataWritten;
    if (m_pEnd >= (m_pBase + m_dwSize)) {
      m_pEnd = m_pBase;
    }

    // Copy data in lower part of ring buffer
    dwDataToWrite = dwLen - dwDataWritten;
    if (dwDataToWrite) {
      pData += dwDataWritten;
      memcpy(m_pEnd, pData, dwDataToWrite);
      dwDataWritten += dwDataToWrite;
      m_pEnd += dwDataToWrite;
      if (m_pEnd >= (m_pBase + m_dwSize)) {
        m_pEnd = m_pBase;
      }
    }

    // Adjust the read buffer level
    m_dwLevel += dwDataWritten;

    // Update internal events
    SetEvent(m_hUsedBytes);
    if (m_dwLevel == m_dwSize) {
      ResetEvent(m_hFreeBytes);
    }
  }

  return dwDataWritten;
}

//////////////////////////////////////////////////////////////////////////////
//
// Read data from the ring buffer for incoming data.
//
// Parameters:
// pData: The buffer receiving the data.
// dwLen: The number of bytes to read.
//
// Return:
// The number of actually read bytes which can be less than the length passed
// in dwLen if the read buffer is empty. In case of an error -1 is returned.
//
//////////////////////////////////////////////////////////////////////////////
DWORD CRingBuffer::Read(PBYTE pData, DWORD dwLen) {
DWORD dwDataToRead;
DWORD dwDataRead = 0;

  SYNCBLOCK(m_csLock);

  if (!m_pBase) return (DWORD)-1;

  // Adjust number of bytes to write to available data size
  dwLen = min(dwLen, m_dwLevel);

  if (dwLen) {
    // Copy data from upper part of ring buffer
    dwDataToRead = min(dwLen, (DWORD)((m_pBase + m_dwSize) - m_pStart));
    memcpy(pData, m_pStart, dwDataToRead);
    dwDataRead = dwDataToRead;
    m_pStart += dwDataRead;
    if (m_pStart >= (m_pBase + m_dwSize)) {
      m_pStart = m_pBase;
    }

    // Copy data from lower part of ring buffer
    dwDataToRead = dwLen - dwDataRead;
    if (dwDataToRead) {
      pData += dwDataRead;
      memcpy(pData, m_pStart, dwDataToRead);
      dwDataRead += dwDataToRead;
      m_pStart += dwDataToRead;
      if (m_pStart >= (m_pBase + m_dwSize)) {
        m_pStart = m_pBase;
      }
    }

    // Adjust the read buffer level
    m_dwLevel = m_dwLevel - dwDataRead;

    // Update internal events
    SetEvent(m_hFreeBytes);
    if (!m_dwLevel) {
      ResetEvent(m_hUsedBytes);
    }
  }

  return dwDataRead;
}

//////////////////////////////////////////////////////////////////////////////
//
// Get the current size of the ring buffer.
//
// Parameters:
// None.
//
// Return:
// The current size of the ring buffer.
//
//////////////////////////////////////////////////////////////////////////////
DWORD CRingBuffer::GetSize(void) {
  SYNCBLOCK(m_csLock);
  return m_dwSize;
}

//////////////////////////////////////////////////////////////////////////////
//
// Get the current number of unread bytes.
//
// Parameters:
// None.
//
// Return:
// The current number of unread bytes.
//
//////////////////////////////////////////////////////////////////////////////
DWORD CRingBuffer::GetLevel(void) {
  SYNCBLOCK(m_csLock);
  return m_dwLevel;
}

//////////////////////////////////////////////////////////////////////////////
//
// Get the current number of free bytes.
//
// Parameters:
// None.
//
// Return:
// The current number of unread bytes.
//
//////////////////////////////////////////////////////////////////////////////
DWORD CRingBuffer::GetSpace(void) {
  SYNCBLOCK(m_csLock);
  return m_dwSize - m_dwLevel;
}

//////////////////////////////////////////////////////////////////////////////
//
// Abort possible WaitForSpace() call.
//
// Parameters:
// None.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CRingBuffer::AbortWaitForSpace(void) {
  SYNCBLOCK(m_csLock);
  if (!m_pBase) return FALSE;
  SetEvent(m_hFreeBytes);
  if (m_dwLevel == m_dwSize) {
    ResetEvent(m_hFreeBytes);
  }
  return TRUE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Abort possible WaitForData() call.
//
// Parameters:
// None.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CRingBuffer::AbortWaitForData(void) {
  SYNCBLOCK(m_csLock);
  if (!m_pBase) return FALSE;
  SetEvent(m_hUsedBytes);
  if (!m_dwLevel) {
    ResetEvent(m_hUsedBytes);
  }
  return TRUE;
}

//////////////////////////////////////////////////////////////////////////////
//
// CRingBuffer - Private implementation
//
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//
// Wait for an internal event.
//
// Parameters:
// hEvent   : The event to wait for.
// dwTimeout: The timeout in milliseconds.
//
// Return:
// The number of passed milliseconds or -1 in case of an error.
//
//////////////////////////////////////////////////////////////////////////////
DWORD CRingBuffer::WaitForEvent(HANDLE hEvent, DWORD dwTimeout) {
DWORD dwRet;
DWORD dwTime;

  dwTime = GetTickCount();
  dwRet = WaitForSingleObject(hEvent, dwTimeout);
  SYNCBLOCK(m_csLock);
  if (!m_pBase) return (DWORD)-1;
  if (dwRet == WAIT_FAILED) return (DWORD)-1;
  if (dwRet == WAIT_TIMEOUT) return dwTimeout;
  return min(GetTickCount() - dwTime, dwTimeout);
}

//****************************************************************************

//////////////////////////////////////////////////////////////////////////////
//
// Registry Tools
//
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//
// This function reads our driver device context out of the registry. The
// device context is equivalent to our UsbHandling class instance created in
// the USB plug and play function "USBDeviceAttach()" and filed in the
// registry value "ClientInfo" of the driver section.
//
// Parameters:
// ActivePath: The registry path to our driver section.
//
// Return:
// Pointer to our CUsbHandling instance on success, NULL in case of failure.
//
//////////////////////////////////////////////////////////////////////////////
CUsbHandling* GetContextFromReg(LPCTSTR ActivePath) {
CUsbHandling* pRet = NULL;

  if (ActivePath) {
    HKEY hKey;
    if (SUCCEEDED(RegOpenKeyEx(HKEY_LOCAL_MACHINE, ActivePath, 0, 0, &hKey))) {
      DWORD dwVal;
      DWORD dwType = REG_DWORD;
      DWORD dwValLen = sizeof(dwVal);
      if (SUCCEEDED(RegQueryValueEx(hKey, TEXT("ClientInfo"), NULL, &dwType, (LPBYTE)(&dwVal), &dwValLen))) {
        PHCUSBDRV_CONTEXT pContext = (PHCUSBDRV_CONTEXT)dwVal;
        if (IS_VALID_CONTEXT(pContext)) {
          pRet = &(pContext->UsbHandling);
        } else {
          DEBUGMSG(ZONE_INIT | ZONE_ERROR, (TEXT("HcUsb: GetContextFromReg() - Error: -> Invalid device context\r\n")));
        }
      } else {
        DEBUGMSG(ZONE_INIT | ZONE_ERROR, (TEXT("HcUsb: GetContextFromReg() - Error: -> Failed to read \"ClientInfo\" from registry\r\n")));
      }
      RegCloseKey(hKey);
    } else {
      DEBUGMSG(ZONE_INIT | ZONE_ERROR, (TEXT("HcUsb: GetContextFromReg() - Error: -> Failed to open given registry key\r\n")));
    }
  }
  return pRet;
}

