/*****************************************************************************
 * 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.h $
 * $Revision: /main/ms-d/23 $
 * $Date: 2014-03-14 11:53:50 $
 * $Author: mwohler $
 *****************************************************************************
 * Target:
 *  Microsoft Windows CE 4.2 or 5.0
 *****************************************************************************
 * Description:
 *  Cinterion Multiplex Driver useful classes and utility function definitions
 *****************************************************************************
 *
 * $Log: O:/mwohler_z_ms-d/z_ms-d/src/working/Classic/MuxDrv/Util.h$ 
 * 
 * /main/ms-d/23   2014-03-14 11:53:50   mwohler 
 * Use latest version of mux protocol implementation based on development for Miami client driver 
 * 
 * /main/ms-d/22   2013-11-04 09:41:33   mwohler 
 * Merged CommEvent handling from USB driver 
 * 
 * /main/ms-d/21   2011-01-10 15:34:02   ctna0450 
 * Use "m_hPort" as valid flag in "CCommTimeoutSection"
 *
 * /main/ms-d/20   2011-01-07 14:47:28   ctna0450
 * Removed obsolete ";"
 *
 * /main/ms-d/19   2011-01-07 14:09:37   ctna0450
 * Improved CommTimeout handling in SendATCmd()
 *
 * /main/ms-d/18   2009-09-04 12:53:44   ctna0450
 * Tidy up CommEvent handling properly without deadlock and exception
 *
 * /main/ms-d/17   2009-09-02 10:23:25   ctna0450
 * Fixed deadlock in CommEvent handling
 *
 * /main/ms-d/16   2009-08-10 10:02:57   ctna0450
 * Fixed problem with retail trace setting
 *
 * /main/ms-d/15   2009-02-18 11:58:28   ctna0256
 * Set copyright text
 *
 * /main/ms-d/14   2009-02-11 12:16:41   ctna0450
 * Virtualized WaitCommEvent() functionality in buffered parent port
 *
 * /main/ms-d/13   2009-02-10 09:56:00   ctna0450
 * Added buffered parent port as workaround for serial drivers without integrated queue handling
 *
 * /main/ms-d/12   2009-02-02 15:22:28   ctna0450
 * Return FALSE in WaitForEvent() when port is closing
 *
 * /main/ms-d/11   2009-01-19 14:26:18   ctna0450
 * Fixed possible exception during closing port
 *
 * /main/ms-d/10   2009-01-13 14:22:16   ctna0450
 * Make mux port instancable to properly support handling of multiple WaitCommEvent() calls
 *
 * /main/ms-d/9   2008-08-20 11:38:47   ctna0450
 * Prepared for new disclaimer handling
 *
 * /main/ms-d/8   2007-05-08 16:47:15   kr36712
 * IPIS100034485
 * reopen implemented for suspend/resume over usb-driver
 *
 * /main/ms-d/7   2006-06-16 12:10:36   wm36264
 * Intruduced utility functions for active device registry access
 *
 * /main/ms-d/6   2006-05-09 13:52:02   KR36712
 * error correction for debugging
 *
 * /main/ms-d/5   2005-10-24 14:25:53   WM36264
 * Added file descriptions
 *
 * /main/ms-d/4   2005-10-20 13:16:55   WM36264
 * Added proper copyright header
 *
 * /main/ms-d/3   2005-10-10 15:35:59   wm36264
 * Reworked macros for debug output
 *
 * /main/ms-d/2   2005-10-07 16:32:42   wm36264
 * Removed unused class CRingBuffer
 *
 * /main/ms-d/1   2005-10-07 16:30:21   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.h
//
// This file contains the definition of the helper class "CWaitCommEvent"
// used inside the class "CMuxChannel" to handle calls of "WaitCommEvent()"
// and "SetCommMask()".
// Additional this file contains the declarations of the used debug trace
// and some helper functions for regsitry access.
//
//////////////////////////////////////////////////////////////////////////////

#pragma once

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

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

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

#if defined(RETAIL_MUXDRV_OUTPUT) || defined (DEBUG)
void TraceHexData(DWORD dwDbgZone, BYTE* pData, DWORD dwLen);
void TraceCRLFString(DWORD dwDbgZone, LPCTSTR szIntro, LPCSTR szTrace);
#endif // WANT_RETAIL_MUXDRV_OUTPUT

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

BOOL RegGetDeviceDWORD(LPCTSTR szActiveKey, LPCTSTR szName, LPDWORD pdwValue);
BOOL RegGetDeviceString(LPCTSTR szActiveKey, LPCTSTR szName, LPTSTR szValue, DWORD dwLen);

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

BOOL IsUsbPort(LPCTSTR szPort);

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

#define INCREASE_LIST_DIFF 4

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

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

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 CCommTimeoutSection {
public:
  CCommTimeoutSection(HANDLE hPort, COMMTIMEOUTS *pNewTimeouts) {
    m_hPort = INVALID_HANDLE_VALUE;
    if (!GetCommTimeouts (hPort, &m_OldTimeouts)) {
      MUXDBG(ZONE_GENERAL_INFO, (TEXT("MuxDrv: CCommTimeoutSection -> Error in GetCommTimeouts()\r\n")));
    } else {
      if (!SetCommTimeouts(hPort, pNewTimeouts)) {
        MUXDBG(ZONE_GENERAL_INFO, (TEXT("MuxDrv: CCommTimeoutSection -> Error in SetCommTimeouts()\r\n")));
      } else {
        m_hPort = hPort;
      }
    }
  }

  ~CCommTimeoutSection() {
    if (m_hPort != INVALID_HANDLE_VALUE) {
      if (!SetCommTimeouts (m_hPort, &m_OldTimeouts)) {
        MUXDBG(ZONE_GENERAL_INFO, (TEXT("MuxDrv: ~CCommTimeoutSection -> Error in SetCommTimeouts()\r\n")));
      }
    }
  }

private:
  HANDLE             m_hPort;
  COMMTIMEOUTS       m_OldTimeouts;
};

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

class CReservation {
public:
  CReservation() : m_dwReservationCount(0) {
    InitializeCriticalSection(&m_csLock);

    m_hLockEvent        = CreateEvent (NULL, TRUE, TRUE, NULL);
    m_hUnreservedEvent  = CreateEvent (NULL, TRUE, TRUE, NULL);
  }

  ~CReservation() {
    CloseHandle (m_hLockEvent);
    CloseHandle (m_hUnreservedEvent);

    DeleteCriticalSection(&m_csLock);
  }

  void Reserve() {
    WaitForSingleObject (m_hLockEvent, INFINITE);

    EnterCriticalSection(&m_csLock);
    m_dwReservationCount++;
//    MUXDBG(ZONE_GENERAL_INFO, (TEXT("MuxDrv: Reserve() RC=%d\r\n"), m_dwReservationCount));
    LeaveCriticalSection (&m_csLock);
  }

  void Release() {
    EnterCriticalSection(&m_csLock);
    if (m_dwReservationCount > 0) {
      m_dwReservationCount--;
//      MUXDBG(ZONE_GENERAL_INFO, (TEXT("MuxDrv: Release() RC=%d\r\n"), m_dwReservationCount));
      if (m_dwReservationCount == 0) {
        SetEvent (m_hUnreservedEvent);
      }
    } else {
      MUXDBG(ZONE_GENERAL_INFO, (TEXT("MuxDrv: CReservation::Release()-> ERROR\r\n")));
    }
    LeaveCriticalSection (&m_csLock);
  }

  BOOL WaitForUnreserved (DWORD dwTimeOut) {
    if (WaitForSingleObject(m_hUnreservedEvent, 0) != WAIT_OBJECT_0) {
      MUXDBG(ZONE_GENERAL_INFO, (TEXT("MuxDrv: WaitForUnreserved() RC=%d\r\n"), m_dwReservationCount));
      return (WaitForSingleObject(m_hUnreservedEvent, dwTimeOut) == WAIT_OBJECT_0) ? TRUE : FALSE;
    }
    return TRUE;
  }

  void LockReservation() {
    // block reservation
    ResetEvent (m_hLockEvent);

    EnterCriticalSection(&m_csLock);
    if (m_dwReservationCount > 0) {
      // reset signal for unreserved signal
      ResetEvent (m_hUnreservedEvent);
    } else {
      SetEvent (m_hUnreservedEvent);
    }
    LeaveCriticalSection (&m_csLock);
  }

  void UnlockReservation() {
    // unblock reservation
    SetEvent (m_hLockEvent);
  }

private:
  CRITICAL_SECTION    m_csLock;

  DWORD               m_dwReservationCount;

  HANDLE              m_hLockEvent;
  HANDLE              m_hUnreservedEvent;
};

