/*****************************************************************************
 * 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.h $
 * $Revision: /main/ms-d/22 $
 * $Date: 2013-11-01 11:48:44 $
 * $Author: mwohler $
 *****************************************************************************
 * Target:
 *  Microsoft Windows CE 5.0
 *****************************************************************************
 * Description:
 *  Quinn-USB-Driver: Useful classes and utility function
 *  definitions
 *****************************************************************************
 *
 * $Log: O:/mwohler_z_ms-d/z_ms-d/src/working/Quinn/HcUsbDrv/Util.h$ 
 * 
 * /main/ms-d/22   2013-11-01 11:48:44   mwohler 
 * Fixed problem with lost WaitCommEvent() calls in case of very fast consecutive events. 
 * 
 * /main/ms-d/21   2011-02-02 10:27:59   ctna0450 
 * Removed unused USB-Reset workaround for Uranus USB problems
 *
 * /main/ms-d/20   2010-12-13 14:23:35   ctna0152
 * added USB reset handling
 *
 * /main/ms-d/19   2010-10-28 11:34:46   ctna0152
 * bugfix in PointerArray class after code review
 *
 * /main/ms-d/18   2010-08-24 12:08:33   ctna0450
 * Simplified PointerArray class
 *
 * /main/ms-d/17   2010-08-17 14:41:54   ctna0450
 * Use dynamic array for port list to be able to handle more than 4 virtual ports
 *
 * /main/ms-d/16   2010-08-04 13:54:44   ctna0450
 * Unified enumeration process
 *
 * /main/ms-d/15   2009-09-04 12:53:47   ctna0450
 * Tidy up CommEvent handling properly without deadlock and exception
 *
 * /main/ms-d/14   2009-09-02 10:23:29   ctna0450
 * Fixed deadlock in CommEvent handling
 *
 * /main/ms-d/13   2009-02-27 15:15:38   ctna0450
 * Added very useful functionality in CRingBuffer for read/write synchronization
 *
 * /main/ms-d/12   2009-02-18 11:49:56   ctna0256
 * Set copyright text
 *
 * /main/ms-d/11   2009-01-19 14:26:22   ctna0450
 * Fixed possible exception during closing port
 *
 * /main/ms-d/10   2009-01-14 10:47:46   ctna0450
 * Make virtual port instancable to properly support handling of consecutive WaitCommEvent() calls
 *
 * /main/ms-d/9   2008-08-20 11:39:22   ctna0450
 * Prepared for new disclaimer handling
 *
 * /main/ms-d/8   2007-04-12 13:35:56   ht37010
 * added function ReleaseWaitEvents()
 * as workaround for reset on global resume
 *
 * /main/ms-d/7   2006-11-03 17:07:06   wm36264
 * Continued to prepare selective suspend handling
 *
 * /main/ms-d/6   2006-11-01 18:31:17   wm36264
 * Implemented simple timer class for future selective suspend
 *
 * /main/ms-d/5   2006-10-06 18:00:25   wm36264
 * Make virtual ports fully instancable and use different device names for serial, ndis and qmi devices
 *
 * /main/ms-d/4   2006-09-29 15:33:36   wm36264
 * Consider latest findings about QMI and NDIS handling
 *
 * /main/ms-d/3   2006-09-22 14:33:17   wm36264
 * Cosmetics mostly
 *
 * /main/ms-d/2   2006-09-21 17:05:33   wm36264
 * Prepared access to QMI interface
 *
 * /main/ms-d/1   2006-09-18 15:31:38   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.h
//
// This file contains the definition of the helper class "CWaitCommEvent"
// used inside the class "CVirtualPort" to handle calls of "WaitCommEvent()"
// and "SetCommMask()", the helper class "CRingBuffer", some trace functions
// and some useful macros.
//
//////////////////////////////////////////////////////////////////////////////

#pragma once

//////////////////////////////////////////////////////////////////////////////

#define INCREASE_LIST_DIFF  4

#define ARRAYSIZE(a)        (sizeof(a) / sizeof(a[0]))

//////////////////////////////////////////////////////////////////////////////

class CWaitCommEvent {
public:
  CWaitCommEvent();
  ~CWaitCommEvent();
  BOOL  ReleaseWaitEvents(void);
  BOOL  WaitForEvent(PDWORD pdwEvent);
  BOOL  SetWaitEvent(DWORD dwEvent);
  BOOL  SetWaitMask(DWORD dwMask);
  BOOL  GetWaitMask(PDWORD pdwMask);

private:
  HANDLE AddWaitEvent(void);

  typedef struct tagWaitEventInfo {
    HANDLE  hEvent;
    DWORD   dwMask;
    DWORD   dwEvent;
    BOOL    fIsValid;
  } WAIT_EVENT_INFO, *PWAIT_EVENT_INFO;

  CRITICAL_SECTION   m_csLock;
  CRITICAL_SECTION   m_csLockDestruct;
  PWAIT_EVENT_INFO   m_pEventList;
  DWORD              m_dwEventListSize;
  DWORD              m_dwWaitMask;
  DWORD              m_dwPendingEvent;
  DWORD              m_dwWaitForEventCalls;
};

//////////////////////////////////////////////////////////////////////////////

class CWaitCommInstance {
public:
  CWaitCommInstance();
  ~CWaitCommInstance();
  void  AddInstance(LPVOID pInstance);
  void  RemoveInstance(LPVOID pInstance);
  BOOL  ReleaseWaitEvents(LPVOID pInstance);
  BOOL  SetWaitEvent(DWORD dwEvent);
  BOOL  WaitForEvent(LPVOID pInstance, PDWORD pdwEvent);
  BOOL  SetWaitMask(LPVOID pInstance, DWORD dwMask);
  BOOL  GetWaitMask(LPVOID pInstance, PDWORD pdwMask);

private:

  typedef struct tagWaitInstanceInfo {
    LPVOID            pInstance;
    CWaitCommEvent*   pWaitCommEvent;
  } WAIT_INSTANCE_INFO, *PWAIT_INSTANCE_INFO;

  CRITICAL_SECTION     m_csLock;
  PWAIT_INSTANCE_INFO  m_pInstanceList;
  DWORD                m_dwInstanceListSize;
};

//////////////////////////////////////////////////////////////////////////////

class CRingBuffer {
public:
  CRingBuffer();
  ~CRingBuffer();
  BOOL   Resize(DWORD dwSize);
  BOOL   Empty(void);
  DWORD  Write(PBYTE pData, DWORD dwLen);
  DWORD  Read(PBYTE pData, DWORD dwLen);
  DWORD GetSize(void);
  DWORD GetLevel(void);
  DWORD  GetSpace(void);
  DWORD  WaitForSpace(DWORD dwTimeout) { return WaitForEvent(m_hFreeBytes, dwTimeout); };
  DWORD  WaitForData(DWORD dwTimeout) { return WaitForEvent(m_hUsedBytes, dwTimeout); };
  BOOL   AbortWaitForSpace(void);
  BOOL   AbortWaitForData(void);

private:
  DWORD  WaitForEvent(HANDLE hEvent, DWORD dwTimeout);

  CRITICAL_SECTION   m_csLock;
  DWORD              m_dwSize;
  DWORD              m_dwLevel;
  PBYTE              m_pBase;
  PBYTE              m_pStart;
  PBYTE              m_pEnd;
  HANDLE             m_hFreeBytes;
  HANDLE             m_hUsedBytes;
};

//////////////////////////////////////////////////////////////////////////////

class CUsbHandling;
CUsbHandling* GetContextFromReg(LPCTSTR ActivePath);

//////////////////////////////////////////////////////////////////////////////

// Class for creating blocks of code that are wrapped in a critical section
class SYNCBLOCK_Implementation {
public:
  SYNCBLOCK_Implementation(CRITICAL_SECTION& rcs) { m_pcs = &rcs; EnterCriticalSection(m_pcs); }
  ~SYNCBLOCK_Implementation() { LeaveCriticalSection(m_pcs); }
private:
  CRITICAL_SECTION* m_pcs;
};

// Class for creating blocks of code inside a SYNCBLOCK that are NOT wrapped in a critical section
class UNSYNCBLOCK_Implementation {
public:
  UNSYNCBLOCK_Implementation(CRITICAL_SECTION& rcs) { m_pcs = &rcs; LeaveCriticalSection(m_pcs); }
  ~UNSYNCBLOCK_Implementation() { EnterCriticalSection(m_pcs); }
private:
  CRITICAL_SECTION* m_pcs;
};

// Macros to make SYNCBLOCK and UNSYNCBLOCK easier to use
#define SYNCBLOCK(cs)                SYNCBLOCK_Implementation    SYNCBLOCK_Object(cs)
#define SYNCBLOCK2(cs)               SYNCBLOCK_Implementation    SYNCBLOCK2_Object(cs)
#define SYNCBLOCK3(cs)               SYNCBLOCK_Implementation    SYNCBLOCK3_Object(cs)
#define UNSYNCBLOCK(cs)              UNSYNCBLOCK_Implementation  UNSYNCBLOCK_Object(cs)
#define UNSYNCBLOCK2(cs)             UNSYNCBLOCK_Implementation  UNSYNCBLOCK2_Object(cs)
#define UNSYNCBLOCK3(cs)             UNSYNCBLOCK_Implementation  UNSYNCBLOCK3_Object(cs)

//////////////////////////////////////////////////////////////////////////////

#define GROW_SIZE  4

class PointerArray {
public:
  PointerArray() {
    m_pData = NULL; m_iSize = 0; m_iSpace = 0;
  }
  ~PointerArray() {
    if (m_pData) delete[] m_pData;
  }
  BOOL Add(void *p) {
    if (m_iSize == m_iSpace) {
      void **pData = new void*[m_iSpace + GROW_SIZE];
      if (!pData) return FALSE;
      if (m_pData) {
        memcpy(pData, m_pData, m_iSize * sizeof(void*));
        delete[] m_pData;
      }
      m_pData = pData;
      m_iSpace += GROW_SIZE;
    }
    m_pData[m_iSize++] = p;
    return TRUE;
  }
  BOOL Delete(int i) {
    if ((i < 0) || (i >= m_iSize)) return FALSE;
    for (int j = i + 1; j < m_iSize; j++, i++) m_pData[i] = m_pData[j];
    m_iSize--;
    return TRUE;
  }
  BOOL Remove(void *p) {
    return Delete(Find(p));
  }
  int Size() {
    return m_iSize;
  }
  int Find(void *p) {
    for (int i = 0; i < m_iSize; i++) {
      if (m_pData[i] == p) return i;
    }
    return -1;
  }
  void* operator [] (int i) {
    return m_pData[i];
  }

private:
  void  **m_pData;
  int     m_iSize;
  int     m_iSpace;
};

//////////////////////////////////////////////////////////////////////////////

