/*****************************************************************************
 * Copyright (C) Cinterion Wireless Modules GmbH
 *****************************************************************************
 * Project: CE-MUX-Driver
 *****************************************************************************
 * Author : Siemens AG Berlin COM WM RD SW1
 *****************************************************************************
 * $Workfile: O:/mwohler_z_ms-d/z_ms-d/src/working/Classic/MuxDrv/Util.cpp $
 * $Revision: /main/ms-d/29 $
 * $Date: 2014-03-14 11:53:48 $
 * $Author: mwohler $
 *****************************************************************************
 * Target:
 *  Microsoft Windows CE 4.2 or 5.0
 *****************************************************************************
 * Description:
 *  Cinterion Multiplex Driver useful classes and utility function implementations
 *****************************************************************************
 *
 * $Log: O:/mwohler_z_ms-d/z_ms-d/src/working/Classic/MuxDrv/Util.cpp$ 
 * 
 * /main/ms-d/29   2014-03-14 11:53:48   mwohler 
 * Use latest version of mux protocol implementation based on development for Miami client driver 
 * 
 * /main/ms-d/28   2013-11-04 09:41:30   mwohler 
 * Merged CommEvent handling from USB driver 
 * 
 * /main/ms-d/27   2013-10-24 13:36:00   pmackent 
 * Introduce Windows Embedded Compact 2013
 *
 * /main/ms-d/26   2011-03-25 13:55:12   ctna0152
 * bugfix for writing log to file
 *
 * /main/ms-d/25   2011-03-01 12:37:59   ctna0450
 * Added possibility for timestamps in retail trace
 *
 * /main/ms-d/23   2009-09-04 12:53:42   ctna0450
 * Tidy up CommEvent handling properly without deadlock and exception
 *
 * /main/ms-d/22   2009-09-02 10:23:22   ctna0450
 * Fixed deadlock in CommEvent handling
 *
 * /main/ms-d/21   2009-08-10 15:03:07   ctna0450
 * Improved performance of retail file trace
 *
 * /main/ms-d/20   2009-08-10 10:02:54   ctna0450
 * Fixed problem with retail trace setting
 *
 * /main/ms-d/19   2009-02-26 14:06:05   ctna0450
 * Do not leave critical section twice
 *
 * /main/ms-d/18   2009-02-18 11:58:26   ctna0256
 * Set copyright text
 *
 * /main/ms-d/17   2009-02-11 12:16:39   ctna0450
 * Virtualized WaitCommEvent() functionality in buffered parent port
 *
 * /main/ms-d/16   2009-02-10 09:55:58   ctna0450
 * Added buffered parent port as workaround for serial drivers without integrated queue handling
 *
 * /main/ms-d/15   2009-02-02 15:22:23   ctna0450
 * Return FALSE in WaitForEvent() when port is closing
 *
 * /main/ms-d/14   2009-01-19 14:26:16   ctna0450
 * Fixed possible exception during closing port
 *
 * /main/ms-d/13   2009-01-14 10:30:02   ctna0450
 * Added some parameter documentation
 *
 * /main/ms-d/12   2009-01-13 14:22:13   ctna0450
 * Make mux port instancable to properly support handling of multiple WaitCommEvent() calls
 *
 * /main/ms-d/11   2008-08-20 11:38:30   ctna0450
 * Prepared for new disclaimer handling
 *
 * /main/ms-d/10   2007-06-11 17:53:23   kr36712
 * correction:
 * ParseEscape()
 * Dlci_EmptyQueues() for protocol version 4
 * AddWaitEvent()
 *
 * /main/ms-d/9   2006-06-16 12:10:34   wm36264
 * Intruduced utility functions for active device registry access
 *
 * /main/ms-d/8   2006-05-09 13:52:04   KR36712
 * error correction for debugging
 *
 * /main/ms-d/7   2006-05-02 14:31:05   KR36712
 * debug feature implemented (port, file)
 *
 * /main/ms-d/6   2005-10-24 14:25:15   WM36264
 * Added file descriptions
 *
 * /main/ms-d/5   2005-10-20 13:16:16   WM36264
 * Added proper copyright header
 *
 * /main/ms-d/4   2005-10-10 15:47:15   wm36264
 * Reworked macros for debug output
 *
 * /main/ms-d/2   2005-10-07 16:32:45   wm36264
 * Removed unused class CRingBuffer
 *
 * /main/ms-d/1   2005-10-07 16:29:56   wm36264
 * First version of Windows CE Muxer
 *
*/

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

//////////////////////////////////////////////////////////////////////////////
//
// Util.cpp
//
// This file contains the implementation of the helper class "CWaitCommEvent"
// used inside the class "CMuxChannel" to handle calls of "WaitCommEvent()"
// and "SetCommMask()".
// Additional this file contains the implementation of the used debug trace
// and some helper functions for regsitry access.
//
//////////////////////////////////////////////////////////////////////////////

#include "precomp.h"

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

//////////////////////////////////////////////////////////////////////////////
//
// Trace raw hex data.
//
// Parameters:
// pData: Data to be traced.
// dwLen: The number of bytes to be traced.
//
// Return:
// None.
//
//////////////////////////////////////////////////////////////////////////////
#if defined(RETAIL_MUXDRV_OUTPUT) || defined(DEBUG)
void TraceHexData(DWORD dwDbgZone, BYTE* pData, DWORD dwLen) {
  if (dwDbgZone) {
    WCHAR  szOut[512], szNum[16];
    DWORD  i, j;
    DWORD  dwCharsPerLine = 16;

    for (i = 0; i < dwLen; i += dwCharsPerLine) {
      wcscpy(szOut, TEXT("MuxDrv:"));
      for (j = 0; (j < dwCharsPerLine) && ((i + j) < dwLen); j++) {
#if (WINCEOSVER >= 600)
        swprintf_s(szNum, sizeof(szNum), TEXT(" 0x%02X"), pData[i + j]);
#else
        swprintf(szNum, TEXT(" 0x%02X"), pData[i + j]);
#endif
        wcscat(szOut, szNum);
      }
      wcscat(szOut, TEXT("\r\n"));
      MUXDBG(dwDbgZone, (szOut));
    }
  }
}
#endif // RETAIL_MUXDRV_OUTPUT

//////////////////////////////////////////////////////////////////////////////
//
// Format a CHAR string into a WCHAR string replacing '\r' and '\n' with
// visable chars
//
//
// Parameters:
// szIntro: The text to be displayed at the beginning.
// szTrace: The CHAR string to be traced with replaced <cr><lf>
//
// Return:
// None.
//
//////////////////////////////////////////////////////////////////////////////
#if defined(RETAIL_MUXDRV_OUTPUT) || defined(DEBUG)
void TraceCRLFString(DWORD dwDbgZone, LPCTSTR szIntro, LPCSTR szTrace) {
  if (dwDbgZone) {
    const WCHAR  szCR[] = TEXT("<cr>");
    const WCHAR  szLF[] = TEXT("<lf>");
    WCHAR        szBuf[256];
    UINT         iLenCR = wcslen(szCR);
    UINT         iLenLF = wcslen(szLF);
    UINT         iPrint;
    UINT         i;
    const UINT   iMaxCharsPerLine = 200;

    if (!szIntro) {
      wcscpy(szBuf, TEXT("MuxDrv: "));
    } else {
      wcscpy(szBuf, szIntro);
    }
    iPrint = wcslen(szBuf);
    for (i = 0; i < strlen(szTrace); i++) {
      // Check if we have to start a new line
      if (iPrint >= iMaxCharsPerLine) {
        wcscpy(&szBuf[iPrint], TEXT("\r\n"));
        MUXDBG(dwDbgZone, (szBuf));
        wcscpy(szBuf, TEXT("MuxDrv: ... "));
        iPrint = wcslen(szBuf);
      }

      // Check for chars to be replaced
      if (szTrace[i] == '\r') {
        wcscpy(&szBuf[iPrint], szCR);
        iPrint += iLenCR;
      } else if (szTrace[i] == '\n') {
        wcscpy(&szBuf[iPrint], szLF);
        iPrint += iLenLF;
      } else {
        szBuf[iPrint] = (WCHAR)szTrace[i];
        iPrint++;
        szBuf[iPrint] = TEXT('\0');
      }
    }
    wcscpy(&szBuf[iPrint], TEXT("\r\n"));
    MUXDBG(dwDbgZone, (szBuf));
  }
}
#endif // RETAIL_MUXDRV_OUTPUT

//////////////////////////////////////////////////////////////////////////////
//
// Open and inintialize the serial port used for debug traces.
//
// Parameters:
// None.
//
// Return:
// The handle of the opened port on success, "INVALID_HANDLE_VALUE" in case
// of failure.
//
//////////////////////////////////////////////////////////////////////////////
#ifdef RETAIL_MUXDRV_OUTPUT_PORT
HANDLE MuxPortTraceOpen(void) {
HANDLE        hPort;
DCB           Dcb;
COMMTIMEOUTS  CommTimeouts;

  // Open the parent port
  hPort = CreateFile(RETAIL_MUXDRV_OUTPUT_PORT, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
  if (hPort == INVALID_HANDLE_VALUE) {
    return INVALID_HANDLE_VALUE;
  }

  // Setup the required buffer sizes
  if (!SetupComm(hPort, 32, 2048)) {
    CloseHandle(hPort);
    return INVALID_HANDLE_VALUE;
  }

  // Get the parent port's DCB
  if (!GetCommState(hPort, &Dcb)) {
    CloseHandle(hPort);
    return INVALID_HANDLE_VALUE;
  }

  // Set our default settings
  Dcb.DCBlength          = sizeof(DCB);
  Dcb.BaudRate           = CBR_115200;
  Dcb.fBinary            = TRUE;
  Dcb.fParity            = TRUE;
  Dcb.fOutxCtsFlow       = TRUE;
  Dcb.fOutxDsrFlow       = FALSE;
  Dcb.fDtrControl        = DTR_CONTROL_ENABLE;
  Dcb.fDsrSensitivity    = FALSE;
  Dcb.fTXContinueOnXoff  = TRUE;
  Dcb.fOutX              = FALSE;
  Dcb.fInX               = FALSE;
  Dcb.fErrorChar         = FALSE;
  Dcb.fNull              = FALSE;
  Dcb.fRtsControl        = RTS_CONTROL_ENABLE;
  Dcb.fAbortOnError      = FALSE;
  Dcb.ByteSize           = 8;
  Dcb.Parity             = NOPARITY;
  Dcb.StopBits           = ONESTOPBIT;

  // Set the parent port's DCB
  if (!SetCommState(hPort, &Dcb)) {
    CloseHandle(hPort);
    return INVALID_HANDLE_VALUE;
  }

  // Set the parent port's timeouts
  CommTimeouts.ReadIntervalTimeout          = MAXDWORD;
  CommTimeouts.ReadTotalTimeoutMultiplier   = 0;
  CommTimeouts.ReadTotalTimeoutConstant     = 0;
  CommTimeouts.WriteTotalTimeoutMultiplier  = 0;
  CommTimeouts.WriteTotalTimeoutConstant    = 1000;
  if (!SetCommTimeouts(hPort, &CommTimeouts)) {
    CloseHandle(hPort);
    return INVALID_HANDLE_VALUE;
  }

  return hPort;
}
#endif // RETAIL_MUXDRV_OUTPUT_PORT

//////////////////////////////////////////////////////////////////////////////
//
// Write formatted output to serial trace port or trace file.
// To avoid unnecessary trace traffic WCHAR strings are converted to char
// strings before they are sent over the serial interface.
//
// Parameters:
// lpszFmt: Pointer to fromat control string.
// ...    : Optional arguments (see e.g. "printf()").
//
// Return:
// The number of written characters.
//
//////////////////////////////////////////////////////////////////////////////
int MuxTrace(LPCTSTR lpszFmt, ...) {
int iRet = 0;

#ifndef DEBUG
#if defined(RETAIL_MUXDRV_OUTPUT_PORT) || defined(RETAIL_MUXDRV_OUTPUT_FILE)
static CRITICAL_SECTION  csTrace;
static TCHAR             Buffer[RETAIL_DEBUG_BUFSIZE];
static va_list           ap;
static DWORD             dwWritten;
static size_t            len;
static BOOL              fFirstCall = TRUE;
#endif // defined(RETAIL_MUXDRV_OUTPUT_PORT) || defined(RETAIL_MUXDRV_OUTPUT_FILE)

#ifdef RETAIL_MUXDRV_OUTPUT_PORT
static HANDLE            hPort = INVALID_HANDLE_VALUE;
#endif // RETAIL_MUXDRV_OUTPUT_PORT

#ifdef RETAIL_MUXDRV_OUTPUT_FILE
static HANDLE            hFile = INVALID_HANDLE_VALUE;
static DWORD g_dwTraceFileLength = 0;
#endif // RETAIL_MUXDRV_OUTPUT_FILE

#if defined(RETAIL_MUXDRV_OUTPUT_PORT) || defined(RETAIL_MUXDRV_OUTPUT_FILE)
  if (!lpszFmt) {
    if (fFirstCall) {
      // We're called for the first time
      InitializeCriticalSection(&csTrace);
      EnterCriticalSection(&csTrace);
#ifdef RETAIL_MUXDRV_OUTPUT_PORT
      hPort = MuxPortTraceOpen();
#endif // RETAIL_MUXDRV_OUTPUT_PORT
#ifdef RETAIL_MUXDRV_OUTPUT_FILE
#ifndef RETAIL_MUXDRV_KEEP_FILE_ON_BOOT
      CloseHandle(CreateFile(RETAIL_MUXDRV_OUTPUT_FILE, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS | TRUNCATE_EXISTING, 0, NULL));
#endif // ifndef RETAIL_MUXDRV_KEEP_FILE_ON_BOOT
#endif // RETAIL_MUXDRV_OUTPUT_FILE
      fFirstCall = FALSE;
      LeaveCriticalSection(&csTrace);
    } else {
      // We're called for the last time
      EnterCriticalSection(&csTrace);
#ifdef RETAIL_MUXDRV_OUTPUT_PORT
      CloseHandle(hPort);
      hPort = INVALID_HANDLE_VALUE;
#endif // RETAIL_MUXDRV_OUTPUT_PORT
#ifdef RETAIL_MUXDRV_OUTPUT_FILE
      CloseHandle(hFile);
      hFile = INVALID_HANDLE_VALUE;
#endif // RETAIL_MUXDRV_OUTPUT_FILE
      fFirstCall = TRUE;
      LeaveCriticalSection(&csTrace);
      DeleteCriticalSection(&csTrace);
    }
  } else {
#ifdef RETAIL_MUXDRV_OUTPUT_FILE
    DWORD   dwFilePos;
#endif
    // We're asked to do some debug trace
    SYNCBLOCK(csTrace);
    iRet = sprintf((char*)Buffer, "%8d TID:%8x ", GetTickCount(), GetCurrentThreadId());

#ifdef RETAIL_MUXDRV_OUTPUT_PORT
    WriteFile(hPort, Buffer, strlen((char*)Buffer), &dwWritten, NULL);
#endif // RETAIL_MUXDRV_OUTPUT_PORT
#ifdef RETAIL_MUXDRV_OUTPUT_FILE
    hFile = CreateFile(RETAIL_MUXDRV_OUTPUT_FILE, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, 0, NULL);
    dwFilePos = SetFilePointer(hFile, 0, 0, FILE_END);
    WriteFile(hFile, Buffer, strlen((char*)Buffer), &dwWritten, NULL);
    if (g_dwTraceFileLength != 0 && g_dwTraceFileLength < dwFilePos + (DWORD)iRet) {
      // Maximum file length reached, delete file content and begin again.
      (void)SetFilePointer(hFile, 0, 0, FILE_BEGIN);
      (void)SetEndOfFile(hFile);
    }
#endif // RETAIL_MUXDRV_OUTPUT_FILE

    va_start(ap, lpszFmt);
    iRet += _vsntprintf(Buffer, RETAIL_DEBUG_BUFSIZE - 1, lpszFmt, ap);
    va_end(ap);
    len = _tcslen(Buffer);
    if (sizeof(char) != sizeof(TCHAR)) {
      DWORD i;
      for (i = 0; i <= len; i++) {
        ((char*)Buffer)[i] = (char)(Buffer[i]);
      }
    }

#ifdef RETAIL_MUXDRV_OUTPUT_PORT
    WriteFile(hPort, Buffer, len, &dwWritten, NULL);
#endif // RETAIL_MUXDRV_OUTPUT_PORT
#ifdef RETAIL_MUXDRV_OUTPUT_FILE
    WriteFile(hFile, Buffer, len, &dwWritten, NULL);
#endif // RETAIL_MUXDRV_OUTPUT_FILE
    if ((((char*)Buffer)[len - 1] != '\r') && (((char*)Buffer)[len - 1] != '\n')) {
#ifdef RETAIL_MUXDRV_OUTPUT_PORT
      WriteFile(hPort, "\r\n", 2, &dwWritten, NULL);
#endif // RETAIL_MUXDRV_OUTPUT_PORT
#ifdef RETAIL_MUXDRV_OUTPUT_FILE
      WriteFile(hFile, "\r\n", 2, &dwWritten, NULL);
#endif // RETAIL_MUXDRV_OUTPUT_FILE
    }
#ifdef RETAIL_MUXDRV_OUTPUT_FILE
    CloseHandle(hFile);
#endif // RETAIL_MUXDRV_OUTPUT_FILE
  }

#endif // defined(RETAIL_MUXDRV_OUTPUT_PORT) || defined(RETAIL_MUXDRV_OUTPUT_FILE)
#endif // DEBUG

  return iRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Write formatted output to debug trace port.
//
// Parameters:
// lpszFmt: Pointer to fromat control string.
// ...    : Optional arguments (see e.g. "printf()").
//
//////////////////////////////////////////////////////////////////////////////
#ifdef RETAIL_MUXDRV_INCLUDE_TIMESTAMPS
void MuxRetailTraceWithTimestamps(LPCTSTR lpszFmt, ...) {
va_list  ap;
WCHAR   *pWC;
WCHAR    wszBuffer[RETAIL_DEBUG_BUFSIZE];
WCHAR    UniBuffer2[RETAIL_DEBUG_BUFSIZE];

  wcsncpy(UniBuffer2, lpszFmt, RETAIL_DEBUG_BUFSIZE);
  do {
    pWC = wcsstr(UniBuffer2, L"%a");
    if (pWC != NULL) {
      wcsncpy(pWC, L"%S", 2);
    }
  }
  while (pWC != NULL);
  va_start(ap, lpszFmt);
  wvsprintf(wszBuffer, UniBuffer2, ap);
  va_end(ap);

#ifdef REATIL_MUXDRV_RTC_TIMESTAMPS
  SYSTEMTIME  sTime;
  GetLocalTime(&sTime);
  NKDbgPrintfW(L"%02d:%02d:%02d:%02d: %s", sTime.wDay, sTime.wHour, sTime.wMinute, sTime.wSecond ,wszBuffer);
#else
  NKDbgPrintfW(L"%06d %s", GetTickCount() % 1000000, wszBuffer);
#endif
}
#endif

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

//////////////////////////////////////////////////////////////////////////////
//
// Read a DWORD value from an active device's registry key.
//
// Parameters:
// szActiveKey: Path of the device active registry key.
// szName     : Name of the registry value.
// pdwValue   : Pointer to the buffer receiving the read value (out).
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL RegGetDeviceDWORD(LPCTSTR szActiveKey, LPCTSTR szName, LPDWORD pdwValue) {
HKEY   hRegKey;
DWORD  dwValueType;
DWORD  dwDataSize;
BOOL   fRet;

  fRet = FALSE;
  hRegKey = OpenDeviceKey(szActiveKey);
  if (hRegKey && (hRegKey != INVALID_HANDLE_VALUE)) {
    dwDataSize = sizeof(DWORD);
    if (!RegQueryValueEx(hRegKey, szName, NULL, &dwValueType, (LPBYTE)pdwValue, &dwDataSize)) {
      if ((dwValueType == REG_DWORD) && (dwDataSize == sizeof(DWORD))) {
        fRet = TRUE;
      }
    }
    RegCloseKey(hRegKey);
  }
  return fRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Read a string value from an active device's registry key.
//
// Parameters:
// szActiveKey: Name of the device active registry key.
// szName     : Name of the registry value.
// pdwValue   : Pointer to the buffer receiving the read string (out).
// dwLen      : Length in bytes of the string buffer including terminating
//              '\0'.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL RegGetDeviceString(LPCTSTR szActiveKey, LPCTSTR szName, LPTSTR szValue, DWORD dwLen) {
HKEY   hRegKey;
DWORD  dwValueType;
BOOL   fRet;

  fRet = FALSE;
  hRegKey = OpenDeviceKey(szActiveKey);
  if (hRegKey && (hRegKey != INVALID_HANDLE_VALUE)) {
    if (!RegQueryValueEx(hRegKey, szName, NULL, &dwValueType, (LPBYTE)szValue, &dwLen)) {
      if ((dwValueType == REG_SZ)) {
        fRet = TRUE;
      }
    }
    RegCloseKey(hRegKey);
  }
  return fRet;
}

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

//////////////////////////////////////////////////////////////////////////////
//
// Step through all subkeys of given registry path in HKEY_LOCAL_MACHINE to
// look for the given prefix value.
//
// Parameters:
// szSubKey: Full registry path in to be searched.
// szPrefix: Prefix to look for.
//
// Return:
// TRUE if the given prefix could be found, FALSE otherwise.
//
//////////////////////////////////////////////////////////////////////////////
BOOL FindRegPrefix(LPCTSTR szSubKey, LPCTSTR szPrefix) {
HKEY hKey;

  if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, szSubKey, 0, KEY_READ, &hKey) == ERROR_SUCCESS) {
    // Firts check the content of the given key itself
    TCHAR szValue[MAX_PATH];
    TCHAR szNextKey[MAX_PATH];
    DWORD dwSize = sizeof(szValue);
    DWORD i = 0;
    if (RegQueryValueEx(hKey, TEXT("Prefix"), NULL, NULL, (LPBYTE)szValue, &dwSize) == ERROR_SUCCESS) {
      if (!_tcsicmp(szValue, szPrefix)) {
        RegCloseKey(hKey);
        return TRUE;
      }
    }
    // Now scan all subkeys
    dwSize = sizeof(szValue);
    while(RegEnumKeyEx(hKey, i++, szValue, &dwSize, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) {
      szNextKey[ARRAYSIZE(szNextKey) - 1] = 0;
      _tcsncpy(szNextKey, szSubKey, ARRAYSIZE(szNextKey) - 1);
      _tcsncat(szNextKey, TEXT("\\"), ARRAYSIZE(szNextKey) - _tcslen(szNextKey) - 1);
      _tcsncat(szNextKey, szValue, ARRAYSIZE(szNextKey) - _tcslen(szNextKey) - 1);
      if (FindRegPrefix(szNextKey, szPrefix)) {
        RegCloseKey(hKey);
        return TRUE;
      }
      dwSize = sizeof(szValue);
    }
    RegCloseKey(hKey);
  }
  return FALSE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Check if a given port is a virtual USB port or not.
//
// Parameters:
// szPort: Name of the port to be checked.
//
// Return:
// TRUE if the given port is a virtual USB port, FALSE otherwise.
//
//////////////////////////////////////////////////////////////////////////////
BOOL IsUsbPort(LPCTSTR szPort) {
TCHAR szPrefix[16];
DWORD i;

  for (i = 0; (i < (ARRAYSIZE(szPrefix) - 1)) && (szPort[i] > TEXT('9')); i++) {
    szPrefix[i] = szPort[i];
  }
  szPrefix[i] = 0;
  if (_tcslen(szPrefix)) {
    if (FindRegPrefix(TEXT("Drivers\\USB\\ClientDrivers"), szPrefix)) {
      MUXDBG(ZONE_GENERAL_INFO, (TEXT("MuxDrv: \"%s\" is an USB port"), szPort));
      return TRUE;
    }
  }
  MUXDBG(ZONE_GENERAL_INFO, (TEXT("MuxDrv: \"%s\" is not an USB port"), szPort));
  return FALSE;
}

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

//////////////////////////////////////////////////////////////////////////////
//
// 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;
}

//////////////////////////////////////////////////////////////////////////////
//
// 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);
}

