/*****************************************************************************
 * Copyright (C) Gemalto M2M GmbH
 *****************************************************************************
 * Project: IMC-RIL-Driver
 *****************************************************************************
 * Author : Wireless Modules RD SW A2
 *****************************************************************************
 * $Workfile: Y:/z_ms-d/src/working/Quinn/RilDrv/comhand.cpp $
 * $Revision: /main/ms-d/56 $
 *     $Date: 2013-07-01 13:09:53 $
 *   $Author: pmackent $
 *****************************************************************************
 * Target:
 *  Microsoft Windows Mobile 5.0
 *****************************************************************************
 * Description:
 *  Gemalto IMC RIL
 *****************************************************************************
 *
 * $Log: Y:/z_ms-d/src/working/Quinn/RilDrv/comhand.cpp$ 
 * 
 * /main/ms-d/56   2013-07-01 13:09:53   pmackent 
 * Added missing variable initialization 
 * 
 * /main/ms-d/55   2013-06-27 16:00:01   pmackent 
 * Adaptions for EHS5 Rel1->Rel2 update
 *
 * /main/ms-d/54   2013-04-29 16:20:11   pmackent
 * Improved deactivation of data connections
 *
 * /main/ms-d/53   2013-03-13 15:04:23   pmackent
 * Additional debug traces for signal critical error.
 *
 * /main/ms-d/52   2013-03-05 11:33:27   pmackent
 * Handle ^SYSSTART URC for EHS5 Rel2
 *
 * /main/ms-d/51   2013-03-04 13:19:29   pmackent
 * Adaptions for EHS5 1.300
 *
 * /main/ms-d/50   2012-10-30 14:36:13   pmackent
 * Fix call status problems (voice and data) in CDMA on WinCE6/7
 *
 * /main/ms-d/49   2012-07-18 11:36:44   pmackent
 * Workaround for the wrong "SIM not inserted" error
 *
 * /main/ms-d/48   2012-02-22 13:26:29   ctna0256
 * Encapsulate use of Intel AT command set
 *
 * /main/ms-d/47   2012-02-17 14:30:22   ctna0256
 * Disable echo after end of data mode
 *
 * /main/ms-d/46   2011-09-26 13:34:12   ctna0256
 * Comply with CE6 warning level 4
 *
 * /main/ms-d/45   2011-09-15 14:05:01   ctna0256
 * remove useless loop
 *
 * /main/ms-d/44   2011-03-09 14:24:33   ctna0256
 * Run code analysis on Windows CE7
 *
 * /main/ms-d/43   2011-01-18 15:06:15   ctna0256
 * New syntax for loading A-GPS files
 *
 * /main/ms-d/42   2010-09-23 13:52:10   ctna0256
 * Workaround for DSR problem in PH8 customer sample
 *
 * /main/ms-d/41   2010-09-03 10:56:56   ctna0256
 * Add GPRS activation notification for CE7
 *
 * /main/ms-d/40   2010-08-25 11:09:21   ctna0256
 * Make code PreFast compatible
 *
 * /main/ms-d/39   2010-08-19 14:29:55   ctna0256
 * Implementation A-GPS for PH8
 *
 * /main/ms-d/38   2010-04-30 15:29:09   ctna0256
 * Automatically switch to 2G for CSD connections (IPIS100064182)
 *
 * /main/ms-d/37   2009-02-11 18:37:23   ctna0450
 * Fixed wrong initialization of csd flag
 *
 * /main/ms-d/35   2009-02-10 11:09:38   ctna0450
 * Broadcast disconnect notifications always when data mode has been left
 *
 * /main/ms-d/34   2009-02-02 15:23:21   ctna0450
 * Fixed restauration of external WaitCommMask after port reopen
 *
 * /main/ms-d/33   2009-01-16 12:44:22   ctna0450
 * New mechanism to prevent PPP data from slip into the AT response buffer
 *
 * /main/ms-d/32   2009-01-14 10:49:55   ctna0450
 * Removed special handling for consecutive WaitCommEvent() calls because mux ports are properly instancable now
 *
 * /main/ms-d/31   2008-08-26 14:26:33   ctna0256
 * Merged RIL_RADIO_RESILIENCE option from Classic-RIL
 *
 * /main/ms-d/30   2008-08-22 10:22:55   ctna0256
 * Set Cinterion copyright text
 *
 * /main/ms-d/29   2008-08-21 09:34:03   ctna0256
 * Prepared for new disclaimer handling
 *
 * /main/ms-d/28   2008-07-22 15:53:13   mp49210
 * Complience to WM7, Lint and PreFast requirements
 *
 * /main/ms-d/27   2008-07-15 14:01:12   mp49210
 * New option: RIL_FILTER_PIN_IN_RETAIL_OUTPUT
 *
 * /main/ms-d/26   2008-06-13 12:12:19   wm36264
 * Toggle DTR to leave data mode before trying +++ sequence
 *
 * /main/ms-d/25   2008-06-12 11:34:21   mp49210
 * New option: retry on invalid index
 *
 * /main/ms-d/24   2008-06-06 15:26:33   mp49210
 * New command option Plan_B
 *
 * /main/ms-d/23   2008-05-30 13:30:54   ht37010
 * bugfix for IPIS 47799:
 * synchronize WaitOnMask() calls from CheckCmdModeThread and the thread that uses the external data port (i.e. PPP)
 *
 * /main/ms-d/22   2008-04-16 16:24:05   mp49210
 * Debug zones for RHA traces
 *
 * /main/ms-d/21   2008-04-03 16:13:02   mp49210
 * Don't repeat AT+CRSM=192 if SIM is not inserted
 *
 * /main/ms-d/20   2008-04-03 14:41:23   mp49210
 * Introduction of new debug zones
 *
 * /main/ms-d/19   2008-03-13 11:53:01   mp49210
 * Introduction of retry while radio off option
 *
 * /main/ms-d/18   2007-11-02 18:34:47   ht37010
 * fix for EPIC 21310: corrected handling of ForcePowerOff during TurnOnModule
 *
 * /main/ms-d/17   2007-11-02 15:50:08   ht37010
 * fix for EPIC21309: passing parameter to avoid waiting for reopen if module is already powered off
 *
 * /main/ms-d/16   2007-10-12 09:54:50   mp49210
 * Workaround for problem with temporary unavailable status line query command
 *
 * /main/ms-d/15   2007-09-11 15:51:23   mp49210
 * Additional notifications at end of PPP packet data connections
 *
 * /main/ms-d/14   2007-08-01 10:33:44   ht37010
 * improved handling if GetModemStatus() fails
 *
 * /main/ms-d/13   2007-07-24 09:26:26   mp49210
 * Additional, optional waiting before sending 2nd part of an AT command
 *
 * /main/ms-d/12   2007-07-18 17:09:39   ht37010
 * IPIS100039395: corrected handling of closing data connection during a suspend-resume-cycle
 *
 * /main/ms-d/11   2007-04-26 14:47:10   kr36712
 * error correction:
 * GPRS Connection establishment during change to suspend mode.
 * SYNCBLOCK(m_csDataMode) removed in the function AbortCommand().
 *
 * /main/ms-d/10   2007-02-20 15:38:52   wm36264
 * Do not abort running command if port is reopened
 *
 * /main/ms-d/9   2007-02-15 12:09:11   wm36264
 * Added port reopen after resume as workaround for USB reset problem
 *
 * /main/ms-d/8   2007-02-09 15:15:37   mp49210
 * Lint compatibility
 *
 * /main/ms-d/7   2007-01-04 13:32:58   wm36264
 * Fixed typo in registry key
 *
 * /main/ms-d/6   2006-11-14 16:59:56   wm36264
 * Tidy up more unused stuff
 *
 * /main/ms-d/5   2006-11-14 14:39:58   wm36264
 * Disposed unused CRilEventLog
 *
 * /main/ms-d/4   2006-11-10 12:28:37   wm36264
 * Block ReadFile() until there is data available
 *
 * /main/ms-d/3   2006-11-09 12:24:22   wm36264
 * Removed some obsolete parameters
 *
 * /main/ms-d/2   2006-11-02 09:30:01   mp49210
 * Addional error code which doesn't allow command repetition.
 *
 * /main/ms-d/1   2006-10-25 16:52:59   wm36264
 * First transformation from classic RIL
 *
*/

//////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2008 Gemalto M2M GmbH
// Copyright (C) 2006-2008 Siemens AG
// Copyright (C) 2003 Intrinsyc Europe Ltd.
//
// Version: 1.0
// Date:    dummy
//
//////////////////////////////////////////////////////////////////////////////

/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Copyright (c) 1995-1999  Microsoft Corporation
--*/

#include "precomp.h"
#ifdef RIL_LAST_ERROR
#include "ccoreutl.h"
#endif

#ifdef UNDER_CE
#include <pegdser.h>
#endif // UNDER_CE

// Defines the maximum number of attempts of sending "AT<cr>" after a command timeout.
// Reconmended value is 1 (one attempt). If set to 0 the module is shut down immediately
// after a command timeout. Higher values can be used for debug purposes.
#define MAX_AT_ATTEMPTS_AFTER_TIMEOUT         1

#ifdef RIL_LAST_ERROR
extern DWORD g_dwLastError;
#endif


//
// Virtual serial device handle ctor
//
CComHandle::CComHandle(COM_PORT_TYPE PortType) :
m_PortType(INVALID_PORT),
m_hDownstream(INVALID_HANDLE_VALUE),
m_hChkCmdPort(INVALID_HANDLE_VALUE),
m_hCComHandleDestroyEvent(NULL),
m_hCommandModeEvent(NULL),
m_hComCancelEvent(NULL),
m_hComInitEvent(NULL),
m_hExternalCancelEvent(NULL),
m_dwDownstreamBaudRate(115200),
m_dwStatQuantum(500),
m_pOwner(NULL),
m_fInited(FALSE),
m_fDataMode(FALSE),
m_fCsdMode(FALSE),
m_fWaitDataMode(FALSE),
m_fCancelledDial(FALSE),
m_dwLastCmdSent(0),
m_dwLastDataSent(0),
m_dwCurrentMask((DWORD)-1),
m_fCommandOngoing(FALSE)
{
    DWORD dwTemp;

    m_PortType = PortType;

    InitializeCriticalSection(&m_csAbortCommand);
    InitializeCriticalSection(&m_csDataMode);
    InitializeCriticalSection(&m_csOwner);
    InitializeCriticalSection(&m_csStats);
    InitializeCriticalSection(&m_csPortHandle);

    // Read the baud rate for the downstream modem
    if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("BaudRate"), &dwTemp))
    {
        m_dwDownstreamBaudRate = dwTemp;
    }

    // Read the data mode interruption time
    if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("DataModeInterruptionQuantum"), &dwTemp))
    {
        m_dwStatQuantum = dwTemp;
    }

    m_hCComHandleDestroyEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

    // Intialize read and write data bickets to 0
    memset(m_rgdwReadStatBits,  0x00, STAT_BUCKETS * sizeof(DWORD));
    memset(m_rgdwWriteStatBits, 0x00, STAT_BUCKETS * sizeof(DWORD));

    // Set the initial timestamps for com activity stats
    m_dwReadStatTimestamp = GetTickCount();
    m_dwWriteStatTimestamp = m_dwReadStatTimestamp;
}


//
// Virtual serial device handle dtor
//
CComHandle::~CComHandle()
{
    if (m_hCComHandleDestroyEvent) {
        (void)SetEvent(m_hCComHandleDestroyEvent);
        m_hCComHandleDestroyEvent = NULL;
    }

    (void)CloseDownstreamPort(FALSE);

    if (m_hCommandModeEvent)
    {
        (void)CloseHandle(m_hCommandModeEvent);
        m_hCommandModeEvent = NULL;
    }

    if (m_hComCancelEvent)
    {
        (void)CloseHandle(m_hComCancelEvent);
        m_hComCancelEvent = NULL;
    }

    if (m_hComInitEvent)
    {
        (void)CloseHandle(m_hComInitEvent);
        m_hComInitEvent = NULL;
    }

    DeleteCriticalSection(&m_csAbortCommand);
    DeleteCriticalSection(&m_csDataMode);
    DeleteCriticalSection(&m_csOwner);
    DeleteCriticalSection(&m_csStats);
    DeleteCriticalSection(&m_csPortHandle);
    m_fInited = FALSE;
}


//
// Virtual serial device handle initialization
//
BOOL CComHandle::Init(void)
{
    if (m_fInited)
    {
        goto Error;
    }

    // Create required events
    m_hCommandModeEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
    if (!m_hCommandModeEvent)
    {
        goto Error;
    }
    m_hComCancelEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
    if (!m_hComCancelEvent)
    {
        goto Error;
    }
    m_hComInitEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (!m_hComInitEvent)
    {
        goto Error;
    }

    m_fInited = TRUE;

    Error:
    return m_fInited;
}


//
// Open downstream port
//
BOOL CComHandle::OpenDownstreamPort(CRilHandle *pRilHandle)
{
#ifdef UNDER_CE
    DWORD dwFlags = 0;
#else   // UNDER_CE
    DWORD dwFlags = FILE_FLAG_OVERLAPPED;
#endif // UNDER_CE

    TCHAR* tszComName;
    DWORD dwTimeout = DEFAULT_PORT_TIMEOUT;
    DWORD nAttempts = 0;
    DWORD nMaxAttempts = DEFAULT_PORT_RETRIES;
    BOOL fRet = FALSE;

    SYNCBLOCK(m_csPortHandle);

    // Get the COM port name
    tszComName = g_ComManager.GetInternalPortName(m_PortType);
    if (!tszComName)
    {
        goto Error;
    }

    if (INVALID_HANDLE_VALUE == m_hDownstream)
    {
        // Get the max number of times we should try to open the COM port
        // Get the amount of time we should sleep between attempts.
        if (pRilHandle != NULL)
            pRilHandle->GetPortOpenTimeouts(&nMaxAttempts, &dwTimeout);

        // Open the COM port
        for (;;)
        {
            if ( g_PowerManager.IsPowerOffForced() == TRUE ) {
              RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: Error - already forced power off\r\n")));
              goto Error;
            }
            // Try to open the COM port
            m_hDownstream = CreateFile(tszComName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, dwFlags, NULL);

            // If we succeeded or exhausted the number of attempts, quit looping
            nAttempts++;
            if ((m_hDownstream != INVALID_HANDLE_VALUE) || (nAttempts >= nMaxAttempts))
            {
                break;
            }

            // Sleep the required amount of time and try again
            Sleep(dwTimeout);
        }

        if (INVALID_HANDLE_VALUE == m_hDownstream)
        {
            goto Error;
        }

        if (!InitCommState(NULL))
        {
            goto Error;
        }

        if (!ResetEvent(m_hComCancelEvent))
        {
            goto Error;
        }

        if (!SetEvent(m_hComInitEvent))
        {
            goto Error;
        }
    }

    if (g_ComManager.IsDataPort(m_PortType))
    {
        // Create 2nd COM port instance for undisturbed WaitCommEvent() calls to wait for command mode
        if (INVALID_HANDLE_VALUE == m_hChkCmdPort)
        {
            m_hChkCmdPort = CreateFile(tszComName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, dwFlags, NULL);
            if (INVALID_HANDLE_VALUE == m_hChkCmdPort)
            {
                goto Error;
            }

            // Setup wait mask to check for command mode
            if (!SetCommMask(m_hChkCmdPort, CMD_MODE_MASK))
            {
                goto Error;
            }
        }
    }


    fRet = TRUE;

    Error:
    if (!fRet)
    {
        if (INVALID_HANDLE_VALUE != m_hChkCmdPort)
        {
            (void)CloseHandle(m_hChkCmdPort);
            m_hChkCmdPort = INVALID_HANDLE_VALUE;
        }
        if (INVALID_HANDLE_VALUE != m_hDownstream)
        {
            (void)CloseHandle(m_hDownstream);
            m_hDownstream = INVALID_HANDLE_VALUE;
        }
    }
    return fRet;
}


//
// Close downstream port
//
BOOL CComHandle::CloseDownstreamPort(BOOL fKeepData)
{
    SYNCBLOCK(m_csPortHandle);

    if (!m_fInited || (INVALID_HANDLE_VALUE == m_hDownstream)) {
      return FALSE;
    }

    // Make sure that data port has left the data mode
    if (!fKeepData) {
      if (g_ComManager.IsDataPort(m_PortType)) {
        if (RHA_IsRadioPwrOn()) {
          if (FDataMode()) {
            (void)VirtEscapeCommFunction(SETDTR);
            Sleep(200);
            (void)VirtEscapeCommFunction(CLRDTR);
            if (!WaitForCommandMode(10000)) {
              (void)TerminateDataMode();
              if (!WaitForCommandMode(5000)) {
                (void)ExitDataMode();
              }
            }
          }
        } else {
          // do not wait for reopen since radio is off
          (void)ExitDataMode(FALSE);
        }
      }
      m_dwCurrentMask = (DWORD)-1;

      // Abort possible running command
      (void)AbortCommand();
    }

    // Release all the waiting operations
    (void)ResetEvent(m_hComInitEvent);
    if (!fKeepData) {
      (void)SetEvent(m_hComCancelEvent);
    }

    // Close the port instance from the check command mode thread
    if (m_hChkCmdPort != INVALID_HANDLE_VALUE) {
      CloseHandle(m_hChkCmdPort);
      m_hChkCmdPort = INVALID_HANDLE_VALUE;
    }

    // Close the downstream port
    (void)CloseHandle(m_hDownstream);
    m_hDownstream = INVALID_HANDLE_VALUE;

    return TRUE;
}


//
// Wait until a valid downstream port is available or we're cancelled
//
BOOL CComHandle::WaitForDownstreamPort(void)
{
    HANDLE  hEvent[3] = { m_hComInitEvent, m_hCComHandleDestroyEvent, m_hExternalCancelEvent };
    if (m_hCComHandleDestroyEvent && m_hExternalCancelEvent) {
        return (WaitForMultipleObjects(3, hEvent, FALSE, INFINITE) == WAIT_OBJECT_0);
    }
    return FALSE;
}


//
// Thread program checking for command mode during GPRS connections
//
DWORD WINAPI CComHandle::CheckCmdModeThread(LPVOID lpParam) {
    CComHandle*  pPort = (CComHandle*)lpParam;
    DWORD        dwMask;
    DWORD        dwTime;
    DWORD        dwCmdModeStatus = CMD_MODE_STATUS;

    RILRetailTraceZone((ZONE_INFO|ZONE_ATCOMMAND), (TEXT("RilDrv: CComHandle::CheckCmdModeThread : Entered\r\n")));

    // Per default the RIL uses the DCD and DSR line to find out,
    // if the module is in data mode, but DSR is not always
    // available.
    if (DSR_LINE_NOT_ALWAYS_CORRECT)
        dwCmdModeStatus = CMD_MODE_STATUS_NO_DSR;

    // First wait until DSR and CD signals are settled properly to high at begin of data mode
    dwMask = 0;
    dwTime = GetTickCount();
    while ((dwMask & dwCmdModeStatus) != dwCmdModeStatus) {
      if (!pPort->CmdGetCommModemStatus(&dwMask, REOPEN_TIMEOUT + FIRSTCOMSTATUS_TIMEOUT)) {
        RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: CComHandle::CheckCmdModeThread : VirtGetCommModemStatus failed\r\n")));
        (void)pPort->ExitDataMode();
        return (DWORD)-1;
      }
      if ((GetTickCount() - dwTime) > 500) {
        RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: CComHandle::CheckCmdModeThread : Timout waiting for initial modem states\r\n")));
        (void)pPort->ExitDataMode();
        return (DWORD)-2;
      }
      if ((dwMask & dwCmdModeStatus) != dwCmdModeStatus) {
        Sleep(10);
      }
    }

    // Now wait for DSR or CD to become low again signaling end of data mode
    while (pPort->FDataMode()) {
      if (DOWNST_WAITCOMMEVENT(pPort->m_hChkCmdPort, &dwMask)) {
        if (!g_ComManager.WaitReopen(REOPEN_TIMEOUT)) {
           RILRetailTraceZone(ZONE_PORT, (TEXT("RilDrv: CComHandle::CheckCmdModeThread() mask=0x%02X EV_BREAK=%d EV_RXCHAR=%d EV_TXEMPTY=%d EV_CTS=%d EV_DSR=%d EV_RLSD=%d\r\n"), dwMask,
                                          dwMask & EV_BREAK,    dwMask & EV_RXCHAR,
                                          dwMask & EV_TXEMPTY,  dwMask & EV_CTS,
                                          dwMask & EV_DSR,      dwMask & EV_RLSD ));
          // We must ignore the COM event if we have an ongoing reconnect process
          if (pPort->FDataMode()) {
            if (!pPort->CmdGetCommModemStatus(&dwMask, REOPEN_TIMEOUT + FIRSTCOMSTATUS_TIMEOUT)) {
              RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: CComHandle::CheckCmdModeThread : VirtGetCommModemStatus failed\r\n")));
              (void)pPort->ExitDataMode();
              return (DWORD)-3;
            }
            if ((dwMask & dwCmdModeStatus) != dwCmdModeStatus) {
              // Module has left data mode
              RILRetailTraceZone((ZONE_INFO|ZONE_ATCOMMAND), (TEXT("RilDrv: CComHandle::CheckCmdModeThread : Detected command mode\r\n")));
              CRilHandle *pHandle = g_ComManager.GetRILDevice();
              if (pHandle != NULL) {
                // Sometimes the switching back in AT command mode is not correctly timed, so there
                // might get some PPP data into the AT command data. This could cause, that the first
                // AT command on this port won't work. As a workaroud the RIL sends a simple AT
                // and ignores any possible error. This AT has to be send with the CMDOPT_POWER
                // option to get the highest priority. In this way it is guaranted, that it is sent before
                // any other command on this port.
                (void)QueueInternalCmd(HCCMD_DATAPORT_ONLY, pHandle, "AT\r", CMDOPT_POWER, APIID_NONE, 1000, 1, 500);
                // The EHS5 AT Interpreter enables the echo again after end of data mode.
                if (ECHO_SETTING_LOST_AFTER_CSD)
                    (void)QueueInternalCmd(HCCMD_DATAPORT_ONLY, pHandle, "ATE0\r", CMDOPT_POWER, APIID_NONE, 1000, 1, 500);
              }
              (void)pPort->ExitDataMode();
            }
          }
        }
      }
    }

    RILRetailTraceZone((ZONE_INFO|ZONE_ATCOMMAND), (TEXT("RilDrv: CComHandle::CheckCmdModeThread : Left\r\n")));
#ifdef RILDRV_CE7
    CloseHandle(CreateThread(NULL, 0, ReCheckCallStatusThreadProc, (LPVOID)(g_ComManager.GetRILDevice()), 0, 0));
#endif

    return 0;
}


//
// NOTE: multiple calls to this without corresponding calls to ExitDataMode() are OK.
//
BOOL CComHandle::EnterDataMode(void)
{
    if (!g_ComManager.IsDataPort(m_PortType)) {
        return FALSE;
    } else {
        SYNCBLOCK(m_csDataMode);

        // Make sure to block anyone who waits for command mode
        if (!ResetEvent(m_hCommandModeEvent)) {
            return FALSE;
        }

        // Start the thread checking for command mode
        HANDLE hThread = CreateThread(0, 0, CheckCmdModeThread, this, 0, 0);
        if (!hThread) {
            return FALSE;
        }
        (void)CloseHandle(hThread);

        // Enable the internal virtual application data port
        if (!g_ComManager.UnlockPortDevice(m_PortType)) {
            return FALSE;
        }

        // Switch to data mode and store the supplied data in the backup buffer
        m_fDataMode = TRUE;
        m_fWaitDataMode = FALSE;

        // Save information if we're running in CSD mode
        m_fCsdMode = FDataCsdMode();

#ifdef RILDRV_CE7
        CRilHandle* pHandle = g_ComManager.GetRILDevice();
        if (pHandle != NULL) {
            if (!m_fCsdMode && (g_dwCurrentContextID > 0)) {
                // In case of the end of an GPRS connection, we must also send the GPRS disconnect notification.
                SendGPRSActivatedNotification(pHandle, g_dwCurrentContextID);
            }
        }
#endif
    }

    return TRUE;
}


//
//
//
BOOL CComHandle::ExitDataMode( BOOL fReopen )
{
    SYNCBLOCK(m_csDataMode);

    // If data mode already left, do nothing
    if (!m_fDataMode) {
        return TRUE;
    }

    BOOL fRet = TRUE;

    // Disable the internal virtual application data port
    if (!g_ComManager.LockPortDevice(m_PortType, fReopen)) {
        fRet = FALSE;
    }

    // Make sure no handle owns the VSP anymore
    (void)ClearOwner(m_pOwner);

    // Re-initialize COM port for RIL use
    m_dwCurrentMask = (DWORD)-1;
    if (!InitComPortForRIL(NULL, NULL)) {
        fRet = FALSE;
    }

    // We're not in data mode anymore
    m_fDataMode = FALSE;
    m_fWaitDataMode = FALSE;

    // Make sure to release anyone waiting for command mode
    if (!SetEvent(m_hCommandModeEvent)) {
        fRet = FALSE;
    }

    // We also have to inform the OS, that a connection had been lost. The OS will call the RIL to find
    // out, which connection is lost, so we don't need to send any additional information here.
    CRilHandle* pHandle = g_ComManager.GetRILDevice();
    if (pHandle != NULL) {
        (void)pHandle->BroadcastDWORDBlobNotification(RIL_NOTIFY_DISCONNECT, RIL_DISCINIT_REMOTE);
        if (!m_fCsdMode && (g_dwCurrentContextID > 0)) {
            // In case of the end of an GPRS connection, we must also send the GPRS disconnect notification.
            SendGPRSDeactivatedNotification(pHandle, g_dwCurrentContextID);
        }
        if (m_fCsdMode && pHandle->Force2GforCSD())
          (void)QueueInternalCmd(HCCMD_STANDARD, pHandle, "AT^SCFG=\"Radio/Band\",\"127\",\"1\"\r", CMDOPT_POWER, APIID_SCFGRADIOBAND, 0, 0, 0);
    }

    return fRet;
}


//
// Manual termination of data mode
//
BOOL CComHandle::TerminateDataMode(void)
{
  SYNCBLOCK(m_csDataMode);

  // If data mode already left, do nothing
  if (!m_fDataMode) {
    return TRUE;
  }

  BOOL fRet = TRUE;

  // Disable the internal virtual application data port to ensure that no data is sent any more by external applications
  if (!g_ComManager.LockPortDevice(m_PortType)) {
    fRet = FALSE;
  }

  // Re-initialize COM port for RIL use
  m_dwCurrentMask = (DWORD)-1;
  if (!InitComPortForRIL(NULL, NULL)) {
    fRet = FALSE;
  }

  // Send the +++ sequence
  DWORD dwWritten;
  DWORD dwSleep;
  (void)VirtPurgeComm(PURGE_RXCLEAR | PURGE_TXCLEAR);
  dwSleep = GetTickCount() - m_dwLastDataSent;
  if (dwSleep < 1100) {
    Sleep(1100 - dwSleep);
  }
  if (VirtWrite("+++", 3, dwWritten) != TRUE) {
    fRet = FALSE;
  }
  Sleep(1100);
  (void)VirtPurgeComm(PURGE_RXCLEAR | PURGE_TXCLEAR);

  return fRet;
}


//
//
//
BOOL CComHandle::FDataCsdMode(void) {
  if (g_ComManager.GetDataPort() == m_PortType) {
    SYNCBLOCK(m_csDataMode);
    if (m_fDataMode) {
      SYNCBLOCK2(g_CallInfoSection);
      if (g_iNrCallInfo > 0) {
        INT i;
        for (i=0; i<g_iNrCallInfo; i++) {
          if ((g_CallInfo[i].dwStatus==RIL_CALLSTAT_ACTIVE) && (g_CallInfo[i].dwType == RIL_CALLTYPE_DATA)) {
            return TRUE;
          }
        }
      }
    }
  }
  return FALSE;
}


//
//
//
BOOL CComHandle::WaitForCommandMode(DWORD dwTimeOut) const
{
    HANDLE rgEvents[2] = { m_hCommandModeEvent, m_hComCancelEvent };

    if (WAIT_OBJECT_0 != WaitForMultipleObjects(2, rgEvents, FALSE, dwTimeOut)) {
        return FALSE;
    }

    return TRUE;
}


//
//
//
BOOL CComHandle::AbortCommand(void)
{
    // SYNCBLOCK(m_csDataMode);

    // Due to a problem in the case of the abort of a command during the
    // GPRS connection establishment -> syncblock removed.
    // See also HandleRxData() in Atcmd.cpp:
    //   If AbortCommand is called during receipt of the "CONNECT" message,
    //   then the function blocks the processing of the command."CONNECT".
    //   (
    //     blocked in funktion FDataMode()
    //     -> if (!m_pComDevice[PortType]->FDataMode() || RIL_NOTIFY_CONNECT != pRsp->GetNotifyCode())
    //   )
    //   The consequence: The module is connected and the appropriate port is
    //                    in the data mode. But the driver treats the port in
    //                    the command mode -> It comes to the command timeout
    //                    and to switching the module off

    BOOL fRet = FALSE;
    if (!FDataMode() && RHA_IsRadioPwrOn()) {
      SYNCBLOCK(m_csAbortCommand);
      DWORD i;
      // First try for 1 second if we might have a fast command we don't need to abort
      for (i = 0; (i < 10) && m_fCommandOngoing; i++) {
        Sleep(100);
      }
      if (m_fCommandOngoing) {
        RILRetailTraceZone(ZONE_ATCOMMAND, (TEXT("RilDrv: Aborting command (%s)\r\n"), g_ComManager.GetDebugName(m_PortType)));
        if (VirtWrite(ABORT_COMMAND, ABORT_COMMAND_SIZE, i)) {
          if (i == ABORT_COMMAND_SIZE) {
            for (i = 0; i < 1500; i++) {
              Sleep(100);
              if (!m_fCommandOngoing) {
                Sleep(100);
                fRet = TRUE;
                break;
              }
            }
          }
        }
      }
    }
    return fRet;
}


//
//
//
void CComHandle::UpdateComStatData(const BOOL fRead, const DWORD dwBytes)
{
    SYNCBLOCK(m_csStats);

    UINT nBucketsToShift;
    DWORD* pdwBuckets = (fRead ? m_rgdwReadStatBits : m_rgdwWriteStatBits);
    DWORD* pdwLastTime = (fRead ? &m_dwReadStatTimestamp : &m_dwWriteStatTimestamp);
    DWORD dwCurrentTime = GetTickCount();

    if (dwCurrentTime != *pdwLastTime)
    {
        // Determine which bucket the new data goes into
        nBucketsToShift = (dwCurrentTime - *pdwLastTime) / m_dwStatQuantum;

        if (STAT_BUCKETS < nBucketsToShift)
        {
            nBucketsToShift = STAT_BUCKETS;
        }

        if (nBucketsToShift)
        {
            // Shift the data in buckets, if needed
            if (STAT_BUCKETS > nBucketsToShift)
            {
                memmove(pdwBuckets + nBucketsToShift, pdwBuckets, (STAT_BUCKETS - nBucketsToShift) * sizeof(DWORD));
            }

            // Clear out all the new buckets upfront
            memset(pdwBuckets, 0x00, nBucketsToShift * sizeof(DWORD));
        }

        // Place the new data into the first bucket
        *pdwBuckets += (dwBytes * 8);

        // Update the timestamp
        *pdwLastTime = dwCurrentTime;
    }
}


//
//
//
void CComHandle::CalculateComStats(DWORD& rdwReadBitsPerSec, DWORD& rdwWrittenBitsPerSec)
{
    SYNCBLOCK(m_csStats);

    UINT i;
    DWORD dwReadBitsPerQuantum = 0;
    DWORD dwWrittenBitsPerQuantum = 0;
    HANDLE hThread = GetCurrentThread();
    int dwOldPri;

    // Switch to the highest priority
    dwOldPri = GetThreadPriority(hThread);
    (void)SetThreadPriority(hThread, THREAD_PRIORITY_HIGHEST);

    // Update the read and write statistics
    UpdateComStatData(TRUE, 0);
    UpdateComStatData(FALSE, 0);

    // Calculate weighted averages of data in read and write buckets (in bits per m_dwStatQuantum msec)
    for (i = 0; i < STAT_BUCKETS; i++)
    {
        dwReadBitsPerQuantum    += m_rgdwReadStatBits[i]  * (STAT_BUCKETS - i) / STAT_BUCKET_WEIGHTS;
        dwWrittenBitsPerQuantum += m_rgdwWriteStatBits[i] * (STAT_BUCKETS - i) / STAT_BUCKET_WEIGHTS;
    }

    // Convert the averages to bits per sec
    rdwReadBitsPerSec    = dwReadBitsPerQuantum    * 1000 / m_dwStatQuantum;
    rdwWrittenBitsPerSec = dwWrittenBitsPerQuantum * 1000 / m_dwStatQuantum;

    // Switch back to the old priority
    (void)SetThreadPriority(hThread, dwOldPri);
}


//
// Function passed to CQueue::ConditionalGet() below
//
static BOOL FDialAnswerCommand(void* pItem, DWORD /*dwData*/)
{
    CCommand* pCmd = (CCommand*)pItem;
    return (pCmd->FDial() || pCmd->FAnswer());
}


//
//
//
static DWORD WINAPI HangupThreadProc(LPVOID lpParameter)
{
    CCommand* pNextCmd = NULL;
    HANDLE hQuitEvent;
    CComHandle* pComDevice;

    {
        HANGUP_THREAD_DATA* phtd = (HANGUP_THREAD_DATA*)lpParameter;

        hQuitEvent = phtd->hQuitEvent;
        pComDevice = phtd->pComDevice;
        delete phtd;
    }

    HANDLE hEvents[2] = {g_hNewDialOrHangupEvent, hQuitEvent};
    DWORD dwWait;
    for (;;)
    {
        dwWait = WaitForMultipleObjects(2, hEvents, FALSE, INFINITE);
        if (dwWait != WAIT_OBJECT_0)
        {
            break;
        }

        if (SUCCEEDED(g_CmdQ.ConditionalGet(FDialAnswerCommand, 0, pNextCmd, 0)))
        {
            // The first command in the queue is a dial or answer
            // Because it hasn't been issued yet, we can abort it easily by
            // removing the command and faking a response.

            // Fake a NOCARRIER response (acceptable for both failed dial and answer commands)
            pNextCmd->SendResponse(RIL_RESULT_NOCARRIER, NULL, 0);
            delete pNextCmd;
            pNextCmd = NULL;
        }
        else if (g_CmdQ.Peek(pNextCmd) && pNextCmd->FHangup())
        {
            DWORD dwTime;

            // The first command in the queue is a hangup, so the dial/answer has
            // already been issued.  We must abort the ongoing command.
            pComDevice->SetCancelledDial();

            // Send AT/r to abort the call
            // Don't remove the command from the queue; the command thread will wake up later and
            // send an additional ATH\r in case the far end picked up before we got a chance to abort.
            // This abort must not be send MIN_TIME_BEFORE_ABORT after the command was send.
            dwTime = GetTickCount();
            if ((pComDevice->GetLastCmdTime() + MIN_TIME_BEFORE_ABORT > dwTime) && (pComDevice->GetLastCmdTime() < dwTime)) {
                Sleep(pComDevice->GetLastCmdTime() + MIN_TIME_BEFORE_ABORT - dwTime);
                RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: Abort Command delayed by %dms\r\n"), pComDevice->GetLastCmdTime() + MIN_TIME_BEFORE_ABORT - dwTime));
            }
            (void)pComDevice->WriteCmdsToComPort("AT\r", 3, FALSE);
        }
    }

    (void)CloseHandle(hQuitEvent);
    return 0;
}

//
// Send an RIL command to the downstream port
//
BOOL CComHandle::SendRILCmdHandleRsp(CRilHandle* const pRilDevice, CCommand*& rpCmd)
{
    LPSTR       szCmd;
    HRESULT     hr, hrPre;
    CResponse*  pRsp = NULL;
    CResponse*  pRspTmp = NULL;
    HANDLE      hQuitEvent = NULL;
    BOOL        fRet = FALSE;
    BOOL        fUnlocked = FALSE;
    PFN_PRE_CMD_CHECK   pfnPreCheck;

    pfnPreCheck = rpCmd->GetPreCheckFunc();
    hrPre = (pfnPreCheck==NULL ? S_OK : pfnPreCheck(pRilDevice, m_PortType));

    if (rpCmd->FNoOp())
    {
#ifdef RIL_RADIO_RESILIENCE
        // This command is a no-op, so we can avoid sending it, but anyway this is a good opurtunity to check,
        // if the module is still alive.
        if (!RHA_IsRadioPwrOn() && g_fAllowReboot)
        {
            RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: SendRILCmdHandleRsp(NOOP) - Module powered off externally\r\n")));
            g_PowerManager.ForcePowerOff();
            hr = RIL_E_RADIOOFF;
            // Send out cancelled response as a notification
            rpCmd->SendResponse(RIL_RESULT_ERROR, &hr, sizeof(HRESULT));
            goto Error;
        }
#endif

        // Allocate a new response
        pRsp = new CResponse(m_PortType, pRilDevice);
        if (!pRsp)
        {
            // Critically low on memory
            RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: No memory for handle new response (noop)!")));
            SignalCriticalError();
            goto Error;
        }

        // Create an OK response (since the command was a no-op, it can't fail)
        pRsp->MakeOK();

        // We can't possibly fail a no-op
        hr = S_OK;
    }
    else if (FAILED(hrPre))
    {
        // This command failed the pre-check so wie return an error.

        // Allocate a new response
        pRsp = new CResponse(m_PortType, pRilDevice);
        if (!pRsp)
        {
            // Critically low on memory
            RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: No memory for new response (pre-check)!")));
            SignalCriticalError();
            goto Error;
        }

        // Create an OK response (since the command was a no-op, it can't fail)
        (void)pRsp->MakeError(hrPre);

        // Fake an response from the module
        hr = S_OK;
    }
    else if (g_PowerManager.IsRadioOff() && !rpCmd->FInit() && !rpCmd->CmdOptIsSet(CMDOPT_IGNORERADIOOFF))
    {
        // The radio is off and the current command is not acceptable while the radio is in this state.

        // Allocate a new response
        pRsp = new CResponse(m_PortType, pRilDevice);
        if (!pRsp)
        {
            // Critically low on memory
            RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: No memory for new response (radio off)!")));
            SignalCriticalError();
            goto Error;
        }

        // Create an error response to indicate that the radio is off.
        if (!pRsp->MakeError(RIL_E_RADIOOFF))
        {
            goto Error;
        }
    }
    else
    {
        m_fCommandOngoing = TRUE;

        // Here we're going to access the module, check if it is still alive before doing this
        if (!RHA_IsRadioPwrOn())
        {
            ExtRILRetailTraceZone(ZONE_ERROR, TEXT("SendRILCmdHandleRsp(%s) - Module powered off externally, not sending: "), g_ComManager.GetDebugName(m_PortType), rpCmd->GetCmd());
            (void)g_PowerManager.ForcePowerOff();
            hr = RIL_E_RADIOOFF;
            // Send out cancelled response as a notification
            rpCmd->SendResponse(RIL_RESULT_ERROR, &hr, sizeof(HRESULT));
            goto Error;
        }

        // This command wasn't a no-op, so we need to send it
        szCmd = rpCmd->GetCmd();

        // Let RIL know we're waiting for AT response
        pRilDevice->StartWaitingForRsp(rpCmd);

        // We want the response that follows the command we are about to send.
        // Discard any responses that have accidentally queued up.
        while (SUCCEEDED(g_RspQ.Get(pRspTmp, 0)))
        {
            ExtRILRetailTraceZone(ZONE_ERROR, TEXT("SendRILCmdHandleRsp(%s) - Discarding extra queued response: "), g_ComManager.GetDebugName(m_PortType), pRspTmp->GetData());
            delete pRspTmp;
            pRspTmp = NULL;
        }

        // Remember that we're dialing a data connection
        if (rpCmd->FDialData())
        {
            SYNCBLOCK(m_csDataMode);
            m_fWaitDataMode = TRUE;
        }

        // Write the command out to the com port
        UINT iCmdLen = strlen(szCmd);
        if (!WriteCmdsToComPort(szCmd, iCmdLen, rpCmd->CmdOptIsSet(CMDOPT_SUPPRESSLOGGING)))
        {
            // Ignore the error. Even if we know that there was a problem writing to the com port,
            // we don't know what exactly was written nor whether we should expect a response.
            // It's best at this point to wait normally for a response. If no response is generated
            // due to the error, then we'll time out and continue as usual.
        }

        // If we haven't a second command part signal that the command has been sent
        if (!rpCmd->GetCmdPart2() && !fUnlocked)
        {
            fUnlocked = TRUE;
            pRilDevice->UnlockRilCmd();
        }

        // If we've just sent a dial or answer command, prepare to handle a hangup command before getting a response
        if (rpCmd->FDial() || rpCmd->FAnswer())
        {
            // Launch a thread to wait for a hangup
            HANDLE hThread = NULL;
            hQuitEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
            if (hQuitEvent)
            {
                HANGUP_THREAD_DATA* phtd = new HANGUP_THREAD_DATA;
                if (phtd)
                {
                    (void)memset(phtd, 0x00, sizeof(HANGUP_THREAD_DATA));
                    phtd->hQuitEvent = hQuitEvent;
                    phtd->pComDevice = this;
                    phtd->pRilDevice = pRilDevice;
                    phtd->PortType = m_PortType;

                    hThread = CreateThread(NULL, 0, HangupThreadProc, (LPVOID)phtd, 0, NULL);
                }

                if (hThread)
                {
                    // We created the thread
                    // Don't need a handle to the thread...
                    (void)CloseHandle(hThread);
                    // The thread takes care of freeing pthd and closing the event handle
                    // Therefore, don't touch phtd after this point
                }
                else
                {
                    // If we didn't create the thread, clean up and soldier on...
                    delete phtd;
                    phtd=NULL;
                    (void)CloseHandle(hQuitEvent);
                    hQuitEvent=NULL;
                }
            }
        }

        // Get the response out of the Response Queue
        hr = g_RspQ.Get(pRsp, rpCmd->GetTimeout());

        if (SUCCEEDED(hr) && rpCmd->CmdOptIsSet(CMDOPT_INTERMEDIATERESPONSE) && (RIL_RESULT_OK == pRsp->GetNotifyCode()))
        {
            szCmd = rpCmd->GetCmdPart2();
            if (szCmd)
            {
                // Wait a little bit before sending the next part of the command
                Sleep(g_dwWaitBeforeATCommand);

                // If there is a secondary command, send it out now. This is primarily to handle SMS send commands
                // that need an intermediary prompt.
                pRilDevice->StopWaitingForRsp();
                pRilDevice->StartWaitingForRsp(rpCmd);
                if (rpCmd->GetAPIID() == APIID_UPLOAD_AGPS) {
                    WCHAR wszFileName[MAX_PATH];
                    HANDLE hFile;
                    DWORD nFileSize;

                    g_GpsHandling.WaitForAGPSUpload();
                    (void)_snwprintfz(wszFileName, MAX_PATH, L"%S", szCmd);
                    hFile = CreateFile(wszFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
                    if (hFile == INVALID_HANDLE_VALUE) {
                        RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: Cannot read AGPS file %s\r\n"), wszFileName));
                        if (INCLUDE_AGPS_LENGTH_IN_FILE) {
                            BYTE bNull[3];

                            // Generate a 1 byte long dummy GPS file to send.
                            bNull[0] = bNull[2] = 0;
                            bNull[1] = 1;
                            (void)WriteBinaryToComPort(bNull, 3);
                        }
                        else {
                            BYTE *pBuffer;

                            nFileSize = (DWORD)rpCmd->GetParseParam();
                            pBuffer = (BYTE *)AllocBlob(nFileSize);
                            if (pBuffer == NULL) {
                                BYTE bNull[2];
                                DWORD nIdx;

                                bNull[0] = bNull[1] = 0;
                                // Not very elegant to send all bytes one by one, but the memory situation
                                // doesn't allow to handle a buffer.
                                for (nIdx = 0; nIdx < nFileSize; nIdx++)
                                    (void)WriteBinaryToComPort(bNull, 1);
                            }
                            else {
                                // The module now waits for nFileSize bytes to come. Since there is
                                // no file available, the RIL sends a block of zeros instead.
                                memset(pBuffer, 0, nFileSize);
                                (void)WriteBinaryToComPort(pBuffer, nFileSize);
                                FreeBlob(pBuffer);
                            }
                        }
                    }
                    else {
                        BYTE bLen[2];
                        BYTE *pBuffer;

                        nFileSize = GetFileSize(hFile, NULL);
                        bLen[0] = (BYTE)((nFileSize>>8)&0xFF);
                        bLen[1] = (BYTE)(nFileSize&0xFF);
                        pBuffer = (BYTE *)AllocBlob(nFileSize);
                        if (pBuffer == NULL) {
                            BYTE bNull[2];

                            bNull[0] = bNull[1] = 0;
                            RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: Out of memory reading AGPS file\r\n")));
                            if (INCLUDE_AGPS_LENGTH_IN_FILE)
                                (void)WriteBinaryToComPort(bNull, 2);
                            else {
                                DWORD nIdx;

                                // Not very elegant to send all bytes one by one, but the memory situation
                                // doesn't allow to handle a buffer.
                                for (nIdx = 0; nIdx < nFileSize; nIdx++)
                                    (void)WriteBinaryToComPort(bNull, 1);
                            }
                        }
                        else {
                            DWORD dwByteSize = 0;

                            memset(pBuffer, 0, nFileSize);
                            if (!ReadFile(hFile, pBuffer, nFileSize, &dwByteSize, NULL))
                                dwByteSize = 0;
                            if (nFileSize != dwByteSize)
                                RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: AGPS file not completly read\r\n")));

                            if (INCLUDE_AGPS_LENGTH_IN_FILE)
                                (void)WriteBinaryToComPort(bLen, 2);
                            (void)WriteBinaryToComPort(pBuffer, nFileSize);
                            FreeBlob(pBuffer);
                        }
                        CloseHandle(hFile);
                    }
                }
                else {
                    iCmdLen = strlen(szCmd);
                    if (!WriteCmdsToComPort(szCmd, iCmdLen, FALSE))
                    {
                        // Ignore the error. Even if we know that there was a problem writing to the com port,
                        // we don't know what exactly was written nor whether we should expect a response.
                        // It's best at this point to wait normally for a response. If no response is generated
                        // due to the error, then we'll time out and continue as usual.
                    }
                }

                // If it hasn't been done until now signal that the command has been sent
                if (!fUnlocked)
                {
                    fUnlocked = TRUE;
                    pRilDevice->UnlockRilCmd();
                }

                // Now wait for the secondary response which will be the real deal.
                delete pRsp;
                pRsp = NULL;
                hr = g_RspQ.Get(pRsp, rpCmd->GetTimeout());
            }
        }

        // Check if we have been timed out before doing any other error handling
        // to properly handle possibly dead module
        if (RIL_E_TIMEDOUT == hr)
        {
            DWORD dwWritten;
            RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: SendRILCmdHandleRsp(%s) - Command has timed out!\r\n"), g_ComManager.GetDebugName(m_PortType)));
#ifdef RIL_LAST_ERROR
            g_dwLastError = MAKELONG(0, RADIO_TIMEOUTERROR);
#endif
            if (RHA_IsRadioPwrOn()) {
              INT iRetries;
              BOOL bATfailed;

              // Abort possibly still running command
              (void)VirtWrite(ABORT_COMMAND, ABORT_COMMAND_SIZE, dwWritten);
              Sleep(1000);

              // Empty response queue
              while (SUCCEEDED(g_RspQ.Get(pRspTmp, 0)))
              {
                  delete pRspTmp;
                  pRspTmp = NULL;
              }

              iRetries = 0;
              bATfailed = TRUE;
              while (iRetries < MAX_AT_ATTEMPTS_AFTER_TIMEOUT)
              {
                  // Send an extra AT\r to check if the module is still alive
                  pRilDevice->StartWaitingForRsp(NULL);
                  (void)WriteCharsToComPort("AT\r", 3, FALSE);
                  // Wait 1 second (+ 2 seconds to wake up module in +CFUN=7 mode) for the response
                  if (FAILED(g_RspQ.Get(pRspTmp, 3000)))
                  {
                    iRetries += 1;
                  }
                  else
                  {
                    bATfailed = FALSE;
                    break;
                  }
              }
              if (bATfailed)
              {
                  // We timed out again even with the simple AT -> the module must have been powered off
                  m_fCommandOngoing = FALSE;
                  (void)pRilDevice->BroadcastDWORDBlobNotification(RIL_NOTIFY_DEVSPECIFIC_EMERG_SHUTDOWN, EMERG_SHUTDOWN_POWERLOSS);
                  (void)g_PowerManager.ForcePowerOff();
                  hr = RIL_E_RADIOOFF;
              }
              delete pRspTmp;
              pRspTmp = NULL;
            } else {
              // We have been powered off externally
              while (SUCCEEDED(g_RspQ.Get(pRspTmp, 0)))
              {
                  delete pRspTmp;
                  pRspTmp = NULL;
              }
              hr = RIL_E_RADIOOFF;
            }
        }

        // Let RIL know we're not waiting for AT response anymore
        pRilDevice->StopWaitingForRsp();

        // If we lauched a thread to wait for hangup, kill it
        if (hQuitEvent)
        {
            (void)SetEvent(hQuitEvent);
            hQuitEvent = NULL;
        }
        if (SUCCEEDED(hr))
        {
            // There are some cases where the SMS send command will fail
            // before RIL sends the PDU bytes. In this case, the PDU
            // bytes are bogus and an error response is returned from
            // the radio. The following code handles the extra error.
            // Note, however, that if the PDU is bad or if there is
            // problem sending the SMS on the network, then there is no
            // extra error to wait for, so RIL will needlessly block for
            // 5 seconds. This case should be rare enough that it is
            // worth the tradeoff.
            if ((APIID_SENDMSG == rpCmd->GetAPIID()) && (RIL_RESULT_ERROR == pRsp->GetNotifyCode()))
            {
                // Let RIL know we're waiting for AT response
                pRilDevice->StartWaitingForRsp(rpCmd);
                if (SUCCEEDED(g_RspQ.Get(pRspTmp, 5000)))
                {
                    delete pRspTmp;
                    pRspTmp = NULL;
                }
                // Let RIL know we're not waiting for AT response anymore
                pRilDevice->StopWaitingForRsp();
            }

            // If we cancelled the last dial command, turn the OK response into a NOCARRIER
            if (m_fCancelledDial)
            {
                if (rpCmd->FDial() || rpCmd->FAnswer())
                {
                    pRsp->SetCallAborted();
                }
                m_fCancelledDial = FALSE;
            }
            else if (rpCmd->FDial() && (RIL_RESULT_ERROR == pRsp->GetNotifyCode()) && rpCmd->CmdOptIsSet(CMDOPT_LONGDIALSTRING))
            {
                (void)pRsp->MakeError(RIL_E_DIALSTRINGTOOLONG);
            }
        }
        else if (RIL_E_TIMEDOUT == hr)
        {
            // We timed out -> handle timeout and try if module is still alive
            RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: SendRILCmdHandleRsp(%s) - Command has timed out but module is still alive!\r\n"), g_ComManager.GetDebugName(m_PortType)));

            // Send a hint to the response thread that we could have bad data in the response buffer.
            pRilDevice->SetLastCommandTimedOut(m_PortType);
            // Additional operations may be required to cancel timed out dial commands, so we need to return a different error
            if (rpCmd->FDial())
            {
                hr = RIL_E_NORESPONSETODIAL;
            }

            // We fail timed out commands
            delete pRsp;
            pRsp = new CResponse(m_PortType, pRilDevice);
            if (!pRsp || !pRsp->MakeError(RIL_E_TIMEDOUT))
            {
                goto Error;
            }
        }
        // See if we couldn't get the response for some reason
        else if (RIL_E_RADIOOFF == hr)
        {
            // If the module has been switched off signal error to MMI and return
            RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: SendRILCmdHandleRsp(%s) - Module has been switched off\r\n"), g_ComManager.GetDebugName(m_PortType)));
            // Send out cancelled response as a notification
            rpCmd->SendResponse(RIL_RESULT_ERROR, &hr, sizeof(HRESULT));
            goto Error;
        }
        else
        {
            goto Error;
        }

        // Did the command timed out internal?
        if (RIL_E_TIMEDOUT == pRsp->GetError())
        {
            m_fCommandOngoing = FALSE;
        }
    }

    // If the last command set the equipment state (+CFUN),
    // then update the global on/off state.
    if ((APIID_SETEQUIPMENTSTATE == rpCmd->GetAPIID()) && (RIL_RESULT_OK == pRsp->GetNotifyCode()))
    {
        // The radio is now on, so we'd better check to see if the SIM is ready
        if (rpCmd->CmdOptIsSet(CMDOPT_SETRADIOON))
        {
            pRilDevice->StartReadyStateQuery();
        }
        else
        {
            // Radio's off -- reset g_dwReadyState
            pRilDevice->StopReadyStateQuery();
            pRilDevice->SendReadyStateNotification(RIL_READYSTATE_NONE, FALSE);
            UpdateSIMState((DWORD)RIL_E_RADIOOFF);
        }
    }

    // If the last command resulted in error, then attempt
    // to retry the command. If no retry is necessary, then
    // RetryCommandOnError will just return without doing anything.
    if (RIL_RESULT_ERROR == pRsp->GetNotifyCode())
    {
        HRESULT hError = S_OK;

        hError = pRsp->GetError();
        hError = rpCmd->TranslateErrorCode(hError);
        // It doesn't make sense to repeat the command when the error cause
        // is radio off, missing SIM card, invalid index, PIN/PUK required, not
        // found or invalid dial string.
        if ((hError != RIL_E_RADIOOFF || rpCmd->CmdOptIsSet(CMDOPT_RETRYONRADIOOFF))           &&
            (hError != RIL_E_NOTFOUND)     &&
            (hError != RIL_E_SIMNOTINSERTED || WRONG_SIM_NOT_INSERTED_ERROR)     &&
            (hError != RIL_E_INVALIDINDEX || rpCmd->CmdOptIsSet(CMDOPT_RETRYONINVALIDINDEX))       &&
            (hError != RIL_E_DIALSTRINGTOOLONG)  &&
            (hError != RIL_E_SIMPINREQUIRED)  &&
            (hError != RIL_E_SIMPIN2REQUIRED)  &&
            (hError != RIL_E_SIMPUKREQUIRED)  &&
            (hError != RIL_E_SIMPUK2REQUIRED)  &&
            (hError != RIL_E_INCORRECTPASSWORD)  &&
            (hError != RIL_E_INVALIDDIALSTRING))
        {
            if (rpCmd->FPlanB())
            {
                if (RequeueCmdWithDelay(rpCmd, 0))
                {
                    // A successful start of plan B isn't an error but we must not call HandleRsp()
                    // in this case and tidy up the command and response objects
                    delete rpCmd;
                    delete pRsp;
                    rpCmd = NULL;
                    pRsp = NULL;
                    fRet = TRUE;
                    goto Error;
                }
            }
            else
            {
                if (rpCmd->RetryCommandOnError())
                {
                    // A successful retry isn't an error but we must not call HandleRsp()
                    // in this case and tidy up the command and response objects
                    delete rpCmd;
                    delete pRsp;
                    rpCmd = NULL;
                    pRsp = NULL;
                    fRet = TRUE;
                    goto Error;
                }
            }
        }
    }

    // Handle the response
    if (!pRilDevice->HandleRsp(rpCmd, pRsp))
    {
        goto Error;
    }

    fRet = TRUE;

    Error:
    if (!fRet)
    {
        delete rpCmd;
        delete pRsp;
        rpCmd = NULL;
    }
    if (hQuitEvent)
    {
        (void)CloseHandle(hQuitEvent);
    }

    m_fCommandOngoing = FALSE;

    // Make sure that the command handlig is unlocked again
    if (!fUnlocked)
    {
        pRilDevice->UnlockRilCmd();
    }

    return fRet;
}

//
//
//
BOOL CComHandle::WriteCmdsToComPort(LPCSTR szChars, UINT cbToWrite, BOOL fLimitedLogging)
{
    LPCSTR szEnd = szChars+cbToWrite;

    for (;;)
    {
        // Look for a <cr>
        LPCSTR szEndCurr = strchr(szChars,'\r');
        // Write up to the <cr>, or up to end if no <cr> found
        cbToWrite = (szEndCurr) ? ((szEndCurr - szChars) + 1) : (szEnd - szChars);

        // Write command to com port
        if(!WriteCharsToComPort(szChars,cbToWrite, fLimitedLogging))
        {
            return FALSE;
        }

        // Move to beginning of next command
        szChars+=cbToWrite;

        // If we're done, exit
        if (szChars>=szEnd)
        {
            return TRUE;
        }

        // If not done, wait awhile before writing next command
        Sleep(50);
    }
}

//
// Writes Bytes to COM port
//
BOOL CComHandle::WriteBinaryToComPort(BYTE *pBytes, UINT cbToWrite)
{
    UINT cbOffset;
    DWORD dwWritten;
    BOOL fRet = FALSE;
    DWORD dwTimeout;

    RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: sending %d bytes to %s\r\n"), cbToWrite, g_ComManager.GetDebugName(m_PortType)));

    dwTimeout = 0;
    cbOffset = 0;
    while (cbToWrite)
    {
        if (!VirtWrite(pBytes + cbOffset, cbToWrite, dwWritten))
        {
            goto Error;
        }
        cbToWrite -= dwWritten;
        cbOffset += dwWritten;
        if (!dwWritten)dwTimeout++;
        if(dwTimeout > 50)goto Error;
    }
    m_dwLastCmdSent = GetTickCount();
    fRet = TRUE;

    Error:
    if (!fRet)
    {
        RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: Failed to send cmd (%s)\r\n"), g_ComManager.GetDebugName(m_PortType)));
    }
    return fRet;
}

//
//
//
#ifdef RIL_FILTER_PIN_IN_RETAIL_OUTPUT
BOOL CComHandle::WriteCharsToComPort(LPCSTR szChars, UINT cbToWrite, BOOL fLimitedLogging)
#else
BOOL CComHandle::WriteCharsToComPort(LPCSTR szChars, UINT cbToWrite, BOOL /*fLimitedLogging*/)
#endif
{
    UINT cbOffset;
    DWORD dwWritten;
    BOOL fRet = FALSE;
    DWORD dwTimeout;

#ifdef RIL_FILTER_PIN_IN_RETAIL_OUTPUT
    if (fLimitedLogging && strlen(szChars) < MAX_ATCMD_LEN-1)
    {
        char szLogChars[MAX_ATCMD_LEN];
        UINT iPos;
        UINT iPosSkip = 0;
        BOOL bFilterInQuotes = FALSE;

        memset(szLogChars, 0, MAX_ATCMD_LEN);
        // Excpetions: the commands contain digits at the beginning, which are no PIN numbers.
        if (strstr(szChars, "+VTS=") != NULL)
            bFilterInQuotes = TRUE;
        if (strstr(szChars, "ATD**04") != NULL || strstr(szChars, "ATD**05") != NULL)
            iPosSkip = 7;
        if (strstr(szChars, "ATD**03") != NULL)
            iPosSkip = 10;
        if (strstr(szChars, "ATD*#33") != NULL || strstr(szChars, "ATD*#35") != NULL)
            iPosSkip = MAX_ATCMD_LEN;
        if (strstr(szChars, "ATD*33") != NULL || strstr(szChars, "ATD*35") != NULL || strstr(szChars, "ATD#33") != NULL || strstr(szChars, "ATD#35") != NULL)
            iPosSkip = 6;
        if (strstr(szChars, "AT+VTD=") != NULL)
            iPosSkip = 9;
        for (iPos = 0 ; iPos < strlen(szChars); iPos++)
        {
            szLogChars[iPos] = szChars[iPos];
            // Numbers are potential PIN numbers, which shouldn't be logged.
            if (iPos > iPosSkip && szChars[iPos] >= '0' && szChars[iPos] <= '9' && iPos > 0)
            {
                // Exception: parameters with only one digit are regular parameters and should be displayed.
                if ((szChars[iPos-1] >= '0' && szChars[iPos-1] <= '9') || (szChars[iPos+1] >= '0' && szChars[iPos+1] <= '9'))
                    szLogChars[iPos] = 'X';         // this is a PIN number, 'X' it out
                // Special cause: filter also single numbers in quotes
                if (bFilterInQuotes && szChars[iPos-1] == '"' && szChars[iPos+1] == '"')
                    szLogChars[iPos] = 'X';         // this is a number to be filtered, 'X' it out

            }
        }
        ExtRILRetailTraceZone(ZONE_ATCOMMAND, TEXT("Sending security cmd (%s): "), g_ComManager.GetDebugName(m_PortType), szLogChars);
    }
    else
        ExtRILRetailTraceZone(ZONE_ATCOMMAND, TEXT("Sending cmd (%s): "), g_ComManager.GetDebugName(m_PortType), szChars);
#else
    ExtRILRetailTraceZone(ZONE_ATCOMMAND, TEXT("Sending cmd (%s): "), g_ComManager.GetDebugName(m_PortType), szChars);
#endif

    dwTimeout = 0;
    cbOffset = 0;
    while (cbToWrite)
    {
        if (!VirtWrite(szChars + cbOffset, cbToWrite, dwWritten))
        {
            goto Error;
        }
        cbToWrite -= dwWritten;
        cbOffset += dwWritten;
        if (!dwWritten)dwTimeout++;
        if(dwTimeout > 50)goto Error;
    }
    m_dwLastCmdSent = GetTickCount();
    fRet = TRUE;

    Error:
    if (!fRet)
    {
        RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: Failed to send cmd (%s)\r\n"), g_ComManager.GetDebugName(m_PortType)));
    }
    return fRet;
}


//
//
//
BOOL CComHandle::SetOwner(const CComInstanceHandle* const pNewOwner)
{
    SYNCBLOCK(m_csOwner);

    BOOL fRet = FALSE;

    if (m_pOwner)
    {
        // The port is already owned by someone -- can't comply
        goto Error;
    }
    m_pOwner = pNewOwner;
    fRet = TRUE;

    Error:
    return fRet;
}


//
//
//
BOOL CComHandle::ClearOwner(const CComInstanceHandle* const pOwner)
{
    SYNCBLOCK(m_csOwner);

    BOOL fRet = FALSE;

    if (m_pOwner != pOwner)
    {
        // The port is owned by someone else
        goto Error;
    }
    m_pOwner = NULL;
    fRet = TRUE;

    Error:
    return fRet;
}


BOOL CComHandle::InitCommState(DCB* const pdcbOld) const
{
    BOOL fRet = FALSE;
    DCB dcb; memset(&dcb,0,sizeof(dcb)); // zero struct

    // Set communication parameters
    dcb.DCBlength = sizeof(DCB);

    fRet = GetCommState(m_hDownstream, &dcb);
    if (!fRet)
    {
        goto Error;
    }

    // Store the old values, if asked
    if (pdcbOld)
    {
        *pdcbOld = dcb;
    }

    dcb.BaudRate = m_dwDownstreamBaudRate;
    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_HANDSHAKE;
    dcb.fAbortOnError = FALSE;
    dcb.ByteSize = 8;
    dcb.Parity = NOPARITY;
    dcb.StopBits = ONESTOPBIT;

    fRet = SetCommState(m_hDownstream, &dcb);
    if (!fRet)
    {
        goto Error;
    }

    fRet = TRUE;

    Error:
    return fRet;
}

//
// Perform serial port initialization
//
BOOL CComHandle::InitComPortForRIL(DCB* const pdcbOld, COMMTIMEOUTS* const pctOld) const
{
    COMMTIMEOUTS ct; memset(&ct,0,sizeof(ct)); // zero struct
    BOOL fRet = FALSE;

    if (!m_fInited || (INVALID_HANDLE_VALUE == m_hDownstream))
    {
        goto Error;
    }

    if (!InitCommState(pdcbOld))
    {
        goto Error;
    }

    // Set the DTR signal which is left cleared in InitCommState()
    if (!EscapeCommFunction(m_hDownstream, SETDTR))
    {
        goto Error;
    }


    // Set timeouts
    if (!GetCommTimeouts(m_hDownstream, &ct))
    {
        goto Error;
    }

    // Store old values, if asked
    if (pctOld)
    {
        *pctOld = ct;
    }

    ct.ReadIntervalTimeout          = MAXDWORD;
    ct.ReadTotalTimeoutMultiplier   = MAXDWORD;
    ct.ReadTotalTimeoutConstant     = MAXDWORD - 1;
    ct.WriteTotalTimeoutMultiplier  = 0;
    ct.WriteTotalTimeoutConstant    = 3000;

    if (!SetCommTimeouts(m_hDownstream, &ct))
    {
        goto Error;
    }

    // Set the com mask to look for arriving characters
    if (!SetCommMask(m_hDownstream, (m_dwCurrentMask == (DWORD)-1) ? DEFAULT_COM_MASK : m_dwCurrentMask))
    {
        goto Error;
    }

    fRet = TRUE;

    Error:
    return fRet;
}


//
// Serial read
//
BOOL CComHandle::VirtRead(void* pBuf, const UINT cb, DWORD& rdwRead) const {
BOOL   fRet = FALSE;
DWORD  dwDownstreamRead;

  rdwRead = 0;

  if (cb > 0) {
    do {
      if (m_hDownstream != INVALID_HANDLE_VALUE) {
        if (DOWNST_READFILE(m_hDownstream, pBuf, cb, &dwDownstreamRead)) {
          fRet = TRUE;
          rdwRead = dwDownstreamRead;
        }
      }
    } while(!fRet && m_fInited && g_ComManager.WaitReopen(REOPEN_TIMEOUT));
  } else {
    fRet = TRUE;
  }

  return fRet;
}


//
// Serial write
//
BOOL CComHandle::VirtWrite(const void* pBuf, const UINT cb, DWORD& rdwWritten) {
BOOL   fRet = FALSE;
DWORD  dwDownstreamWritten;

  rdwWritten = 0;

  if (cb > 0) {
    do {
      if (m_hDownstream != INVALID_HANDLE_VALUE) {
        if (DOWNST_WRITEFILE(m_hDownstream, pBuf, cb, &dwDownstreamWritten)) {
          fRet = TRUE;
          if (m_fDataMode) {
            m_dwLastDataSent = GetTickCount();
          }
          rdwWritten = dwDownstreamWritten;
        }
      }
    } while(!fRet && m_fInited && g_ComManager.WaitReopen(REOPEN_TIMEOUT));
  } else {
    fRet = TRUE;
  }

  return fRet;
}


//
// Serial IO-control
//
BOOL CComHandle::VirtIOControl(const DWORD dwCode, const BYTE* const pBufIn, const DWORD dwLenIn, BYTE* const pBufOut,
                               const DWORD dwLenOut, DWORD& rdwActualOut, BOOL fLockSignals) {
BOOL fRet = FALSE;

  do {
    if (m_hDownstream != INVALID_HANDLE_VALUE) {
      // Don't let the outside users change DTR because this throws module out of data mode
      if (fLockSignals && ((IOCTL_SERIAL_SET_DTR == dwCode) || (IOCTL_SERIAL_CLR_DTR == dwCode))) {
        return TRUE;
      }

      // Finally call the downstream port
      fRet = DeviceIoControl(m_hDownstream, dwCode, (void*)pBufIn, dwLenIn, pBufOut, dwLenOut, &rdwActualOut, NULL);
    }
  } while(!fRet && m_fInited && g_ComManager.WaitReopen(REOPEN_TIMEOUT));

  // Save wait mask to be able to restore it after possible later reopen
  if (fRet && (IOCTL_SERIAL_SET_WAIT_MASK == dwCode)) {
    if (FDataMode()) {
      m_dwCurrentMask = *(PDWORD)pBufIn;
    }
  }

  return fRet;
}

//
//
//
BOOL CComHandle::VirtWaitCommEvent(DWORD* const pdwMask) const {
BOOL fRet = FALSE;

  do {
    if (m_hDownstream != INVALID_HANDLE_VALUE) {
      fRet = DOWNST_WAITCOMMEVENT(m_hDownstream, pdwMask);
    }
  } while(!fRet && m_fInited && g_ComManager.WaitReopen(REOPEN_TIMEOUT));
  return fRet;
}


//
//
//
BOOL CComHandle::VirtClearCommBreak(BOOL fReopen) const {
BOOL fRet = FALSE;

  do {
    if (m_hDownstream != INVALID_HANDLE_VALUE) {
      fRet = ClearCommBreak(m_hDownstream);
    }
  } while(!fRet && m_fInited && fReopen && g_ComManager.WaitReopen(REOPEN_TIMEOUT));
  return fRet;
}


//
//
//
BOOL CComHandle::VirtClearCommError(DWORD* const pdwErrors, COMSTAT* const pcs, BOOL fReopen) const {
BOOL fRet = FALSE;

  do {
    if (m_hDownstream != INVALID_HANDLE_VALUE) {
      fRet = ClearCommError(m_hDownstream, pdwErrors, pcs);
    }
  } while(!fRet && m_fInited && fReopen && g_ComManager.WaitReopen(REOPEN_TIMEOUT));
  return fRet;
}


//
//
//
BOOL CComHandle::VirtEscapeCommFunction(const DWORD dwFunc) const {
BOOL fRet = FALSE;

  do {
    if (m_hDownstream != INVALID_HANDLE_VALUE) {
      fRet = EscapeCommFunction(m_hDownstream, dwFunc);
    }
  } while(!fRet && m_fInited && g_ComManager.WaitReopen(REOPEN_TIMEOUT));
  return fRet;
}


//
//
//
BOOL CComHandle::VirtGetCommMask(DWORD* const pdwMask) const {
BOOL fRet = FALSE;

  do {
    if (m_hDownstream != INVALID_HANDLE_VALUE) {
      fRet = GetCommMask(m_hDownstream, pdwMask);
    }
  } while(!fRet && m_fInited && g_ComManager.WaitReopen(REOPEN_TIMEOUT));
  return fRet;
}


//
//
//
BOOL CComHandle::VirtSetCommMask(const DWORD dwMask, BOOL fReopen) {
BOOL fRet = FALSE;

  do {
    if (m_hDownstream != INVALID_HANDLE_VALUE) {
      fRet = SetCommMask(m_hDownstream, dwMask);
    }
  } while(!fRet && m_fInited && fReopen && g_ComManager.WaitReopen(REOPEN_TIMEOUT));
  if (fRet && FDataMode()) {
    m_dwCurrentMask = dwMask;
  }
  return fRet;
}


//
//
//
BOOL CComHandle::VirtGetCommModemStatus(DWORD* const pdwStatus, DWORD dwTimeout) const {
BOOL fRet = FALSE;
DWORD dwStartTime = GetTickCount();

  do {
    if (m_hDownstream != INVALID_HANDLE_VALUE) {
        for(;;) {
            fRet = GetCommModemStatus(m_hDownstream, pdwStatus);
            if ( fRet == FALSE && GetLastError() == ERROR_NOT_READY) {
                // after reopening it may take some time until the status is updated
                Sleep( 1000 );
                RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: CComHandle::VirtGetCommModemStatus() retry - GetCommModemStatus\r\n")));
            } else {
                break;
            }
        }
    }
  } while(!fRet && m_fInited && (g_ComManager.WaitReopen(REOPEN_TIMEOUT) || GetTickCount() < dwStartTime + dwTimeout));
  return fRet;
}


//
//
//
BOOL CComHandle::CmdGetCommModemStatus(DWORD* const pdwStatus, DWORD dwTimeout) const {
BOOL fRet = FALSE;
DWORD dwStartTime = GetTickCount();

  do {
    if (m_hChkCmdPort != INVALID_HANDLE_VALUE) {
        for(;;) {
            fRet = GetCommModemStatus(m_hChkCmdPort, pdwStatus);
            if ( fRet == FALSE && GetLastError() == ERROR_NOT_READY) {
                // after reopening it may take some time until the status is updated
                Sleep( 1000 );
                RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: CComHandle::CmdGetCommModemStatus() retry - GetCommModemStatus\r\n")));
            } else {
                break;
            }
        }
    }
  } while(!fRet && m_fInited && (g_ComManager.WaitReopen(REOPEN_TIMEOUT) || GetTickCount() < dwStartTime + dwTimeout));
  return fRet;
}


//
//
//
BOOL CComHandle::VirtGetCommProperties(COMMPROP* const pcp) const {
BOOL fRet = FALSE;

  do {
    if (m_hDownstream != INVALID_HANDLE_VALUE) {
      fRet = GetCommProperties(m_hDownstream, pcp);
    }
  } while(!fRet && m_fInited && g_ComManager.WaitReopen(REOPEN_TIMEOUT));
  return fRet;
}


//
//
//
BOOL CComHandle::VirtGetCommState(DCB* const pdcb) const {
BOOL fRet = FALSE;

  do {
    if (m_hDownstream != INVALID_HANDLE_VALUE) {
      fRet = GetCommState(m_hDownstream, pdcb);
    }
  } while(!fRet && m_fInited && g_ComManager.WaitReopen(REOPEN_TIMEOUT));
  return fRet;
}


//
//
//
BOOL CComHandle::VirtSetCommState(DCB* const pdcb) const {
BOOL fRet = FALSE;

  do {
    if (m_hDownstream != INVALID_HANDLE_VALUE) {
      fRet = SetCommState(m_hDownstream, pdcb);
    }
  } while(!fRet && m_fInited && g_ComManager.WaitReopen(REOPEN_TIMEOUT));
  return fRet;
}


//
//
//
BOOL CComHandle::VirtGetCommTimeouts(COMMTIMEOUTS* const pct) const {
BOOL fRet = FALSE;

  do {
    if (m_hDownstream != INVALID_HANDLE_VALUE) {
      fRet = GetCommTimeouts(m_hDownstream, pct);
    }
  } while(!fRet && m_fInited && g_ComManager.WaitReopen(REOPEN_TIMEOUT));
  return fRet;
}


//
//
//
BOOL CComHandle::VirtSetCommTimeouts(COMMTIMEOUTS* const pct) const {
BOOL fRet = FALSE;

  do {
    if (m_hDownstream != INVALID_HANDLE_VALUE) {
      fRet = SetCommTimeouts(m_hDownstream, pct);
    }
  } while(!fRet && m_fInited && g_ComManager.WaitReopen(REOPEN_TIMEOUT));
  return fRet;
}


//
//
//
BOOL CComHandle::VirtPurgeComm(const DWORD dwFlags, BOOL fReopen) const {
BOOL fRet = FALSE;

  do {
    if (m_hDownstream != INVALID_HANDLE_VALUE) {
      fRet = PurgeComm(m_hDownstream, dwFlags);
    }
  } while(!fRet && m_fInited && fReopen && g_ComManager.WaitReopen(REOPEN_TIMEOUT));
  return fRet;
}


//
//
//
BOOL CComHandle::VirtSetCommBreak(void) const {
BOOL fRet = FALSE;

  do {
    if (m_hDownstream != INVALID_HANDLE_VALUE) {
      fRet = SetCommBreak(m_hDownstream);
    }
  } while(!fRet && m_fInited && g_ComManager.WaitReopen(REOPEN_TIMEOUT));
  return fRet;
}


//
//
//
BOOL CComHandle::VirtSetupComm(const DWORD dwInQueue, const DWORD dwOutQueue) const {
BOOL fRet = FALSE;

  do {
    if (m_hDownstream != INVALID_HANDLE_VALUE) {
      fRet = SetupComm(m_hDownstream, dwInQueue, dwOutQueue);
    }
  } while(!fRet && m_fInited && g_ComManager.WaitReopen(REOPEN_TIMEOUT));
  return fRet;
}


//
//
//
BOOL CComHandle::VirtTransmitCommChar(const char cChar) const {
BOOL fRet = FALSE;

  do {
    if (m_hDownstream != INVALID_HANDLE_VALUE) {
      fRet = TransmitCommChar(m_hDownstream, cChar);
    }
  } while(!fRet && m_fInited && g_ComManager.WaitReopen(REOPEN_TIMEOUT));
  return fRet;
}


BOOL CComHandle::SwitchBackToCommandMode(void)
{
  BOOL fRet = TRUE;

  if (FDataMode()) {
    // Wait if data mode is left external
    if (WaitForCommandMode(10000) != TRUE) {
      // Data mode not left -> leave data mode manually by clearing DTR
      RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: SwitchBackToCommandMode() : Data mode not left external\r\n")));
      // Set DTR and clear DTR to leave data mode
      (void)VirtEscapeCommFunction(SETDTR);
      Sleep(200);
      (void)VirtEscapeCommFunction(CLRDTR);

      // Module is now leaving data mode which should be traced by the CComHandle class
      if (WaitForCommandMode(10000) != TRUE) {
        // For some unknown reason leaving data mode failed -> leave data mode manually
        RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: SwitchBackToCommandMode() : Data mode not left even after clearing DTR\r\n")));
        // Now we try our last fallback solution to leave the data mode
        if (TerminateDataMode() != TRUE) {
          // manual switch to data mode failed, switch port back anyway
          RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: SwitchBackToCommandMode() : Failed to switch manually to command mode, data mode finally NOT left!\r\n")));
          (void)ExitDataMode();
        } else {
          if (WaitForCommandMode(5000) != TRUE) {
            // Leaving data mode finally failed, switch port back anyway
            RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: SwitchBackToCommandMode() : Data mode not left even after manual switch, data mode finally NOT left!\r\n")));
            (void)ExitDataMode();
            fRet = FALSE;
          }
        }
      }
      // Set DTR again for next commad
      (void)VirtEscapeCommFunction(SETDTR);
    }
  }
  return fRet;
}

//
// Virtual serial instance handle ctor
//
CComInstanceHandle::CComInstanceHandle()
: m_pDevice (NULL),
m_fRsaMode(FALSE)
{
}


//
// Virtual serial instance handle dtor
//
CComInstanceHandle::~CComInstanceHandle()
{
    // If this is the owner of the VSP, make sure it doesn't own the VSP anymore
    (void)m_pDevice->ClearOwner(this);
}


//
// Virtual serial instance handle initialization
//
BOOL CComInstanceHandle::Init(CComHandle* const pDevice, const DWORD /*dwAccess*/)
{
    BOOL fRet = FALSE;

    m_pDevice = pDevice;

    // Don't allow VSP to be opened if we're not in data mode or if someone has already opened it
    if (!m_pDevice->FDataMode() || !m_pDevice->SetOwner(this))
    {
        goto Error;
    }

    fRet = TRUE;

    Error:
    return fRet;
}



#ifndef UNDER_CE
//
// Perform overlapped I/O operation (WinNT only)
//
BOOL PerformOverlappedIO(const OVERLAPPED_OPER oper, const HANDLE hCom, void* pbData, UINT cbData, DWORD* pdwData)
{
    // WinNT will only allow one thread at a time toi communicate to a COM port, so
    //    we must use asynchronous IO operations

    OVERLAPPED ol; memset(&ol,0,sizeof(ol)); // zero struct
    DWORD dwBytes;
    BOOL fRet = FALSE;

    ol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (!ol.hEvent)
    {
        goto Error;
    }

    switch (oper)
    {
    case OVERLAPPED_WAIT:
        fRet = WaitCommEvent(hCom, pdwData, &ol);
        break;

    case OVERLAPPED_READ:
        fRet = ReadFile(hCom, pbData, cbData, pdwData, &ol);
        break;

    case OVERLAPPED_WRITE:
        fRet = WriteFile(hCom, pbData, cbData, pdwData, &ol);
        break;
    }

    if (fRet)
    {
        // Operation completed synchronously
        goto Error;
    }
    else if (ERROR_IO_PENDING != GetLastError())
    {
        // Operation failed
        goto Error;
    }

    // Operation is overlapped -- wait for it to complete
    if (!GetOverlappedResult(hCom, &ol, &dwBytes, TRUE))
    {
        goto Error;
    }

    if (oper != OVERLAPPED_WAIT)
    {
        *pdwData = dwBytes;
    }
    fRet = TRUE;

    Error:
    if (ol.hEvent)
    {
        (void)CloseHandle(ol.hEvent);
    }
    return fRet;
}
#endif // UNDER_CE

