/*****************************************************************************
 * Copyright (C) Gemalto M2M GmbH
 *****************************************************************************
 * Project: IMC-RIL-Driver
 *****************************************************************************
 * Author : Wireless Modules RD SW A2
 *****************************************************************************
 * $Workfile: Y:/z_ms-d/src/working/Quinn/RilDrv/atcmd.cpp $
 * $Revision: /main/ms-d/51 $
 *     $Date: 2014-01-31 12:03:40 $
 *   $Author: pmackent $
 *****************************************************************************
 * Target:
 *  Microsoft Windows Mobile 5.0
 *****************************************************************************
 * Description:
 *  Gemalto IMC RIL
 *****************************************************************************
 *
 * $Log: Y:/z_ms-d/src/working/Quinn/RilDrv/atcmd.cpp$ 
 * 
 * /main/ms-d/51   2014-01-31 12:03:40   pmackent 
 * Handle SIM-PIN error for Wrong-CPWD-WA. Disable this WA for PHS8 Rel3
 *
 * /main/ms-d/50   2013-10-01 14:11:32   pmackent
 * Trigger right event in case the error parsing function of an internal blocking AT command modified an error to an OK
 *
 * /main/ms-d/49   2013-05-22 16:51:06   pmackent
 * Prevent NULL-pointer assignment on "NO CARRIER" at "AT"
 *
 * /main/ms-d/48   2013-03-13 15:04:21   pmackent
 * Additional debug traces for signal critical error.
 *
 * /main/ms-d/47   2012-10-08 16:18:16   pmackent
 * Comply to Klocwork checks.
 *
 * /main/ms-d/46   2012-08-13 11:03:45   pmackent
 * Comply to CE6 code analyzer requirements
 *
 * /main/ms-d/45   2012-07-11 15:22:41   pmackent
 * Fix for exception when making a new data connection attempt in CDMA after a failed attempt
 *
 * /main/ms-d/44   2012-04-18 13:46:41   ctna0256
 * Introducing PVS8
 * Implementing CDMA-SMS
 * Various CDMA enhancements
 *
 * /main/ms-d/43   2012-02-22 13:26:21   ctna0256
 * Encapsulate use of Intel AT command set
 *
 * /main/ms-d/42   2012-01-31 16:38:12   ctna0256
 * Added support for EHS5
 *
 * /main/ms-d/41   2011-09-26 13:33:57   ctna0256
 * Comply with CE6 warning level 4
 *
 * /main/ms-d/40   2011-03-25 09:09:27   ctna0256
 * Prevent unnecessary "radio off" error (IPIS100071415)
 *
 * /main/ms-d/39   2011-03-09 14:24:24   ctna0256
 * Run code analysis on Windows CE7
 *
 * /main/ms-d/38   2010-12-15 10:43:43   ctna0256
 * Performed lint check
 *
 * /main/ms-d/37   2010-08-19 14:29:50   ctna0256
 * Implementation A-GPS for PH8
 *
 * /main/ms-d/36   2010-07-22 14:44:42   ctna0256
 * Allow setting of default behaviour for incoming text messages
 *
 * /main/ms-d/35   2010-02-03 10:50:34   ctna0256
 * Introduction of WinCE6 support
 *
 * /main/ms-d/34   2009-10-07 09:22:09   ctna0256
 * Introduce no-operation command queue
 * Support 2nd alpha identifier in SAT setup call (IPIS100055375)
 *
 * /main/ms-d/33   2009-01-16 12:44:17   ctna0450
 * New mechanism to prevent PPP data from slip into the AT response buffer
 *
 * /main/ms-d/32   2008-12-01 11:29:21   ctna0450
 * Fixed problem with mux frame size bigger than read buffer size
 *
 * /main/ms-d/31   2008-11-28 14:13:23   ctna0256
 * Increase buffer size for new mux protocoll
 *
 * /main/ms-d/30   2008-08-22 10:22:26   ctna0256
 * Set Cinterion copyright text
 *
 * /main/ms-d/29   2008-08-21 09:33:49   ctna0256
 * Prepared for new disclaimer handling
 *
 * /main/ms-d/28   2008-07-22 15:53:11   mp49210
 * Complience to WM7, Lint and PreFast requirements
 *
 * /main/ms-d/27   2008-06-06 15:26:31   mp49210
 * New command option Plan_B
 *
 * /main/ms-d/26   2008-04-03 14:41:16   mp49210
 * Introduction of new debug zones
 *
 * /main/ms-d/25   2008-03-14 11:39:34   mp49210
 * IPIS100045649: special handling of BUSY for SAT setup call handling
 *
 * /main/ms-d/24   2008-02-11 09:18:12   mp49210
 * Remove LINT warnings
 *
 * /main/ms-d/23   2008-02-04 14:56:10   mp49210
 * Send SMS workaround also for command retries
 *
 * /main/ms-d/22   2008-01-29 11:50:45   mp49210
 * Wrong use of tabs
 *
 * /main/ms-d/21   2008-01-24 15:57:35   kr36712
 * integration GPS for MTLR and emergency call
 *
 * /main/ms-d/20   2007-11-02 15:16:54   mp49210
 * Check SIM/USIM after successful At+CPIN? command only
 *
 * /main/ms-d/19   2007-07-11 15:11:25   mp49210
 * Optional additional waiting period before sending AT commands
 *
 * /main/ms-d/18   2007-06-28 09:58:43   mp49210
 * Close handle to threads to prevent handle leaks in memory
 *
 * /main/ms-d/17   2007-05-29 09:36:57   mp49210
 * Workaoround for wrong error code to AT+CPIN?
 *
 * /main/ms-d/16   2007-02-09 15:15:30   mp49210
 * Lint compatibility
 *
 * /main/ms-d/15   2007-01-22 16:18:31   wm36264
 * Removed workaround for failing 1st at command after data mode
 *
 * /main/ms-d/14   2006-12-27 14:20:12   mp49210
 * Workaround for wrong CME-error at AT+CPBR
 *
 * /main/ms-d/13   2006-11-22 15:43:16   wm36264
 * Avoid obsolete new/delete cycles
 *
 * /main/ms-d/12   2006-11-22 12:21:24   wm36264
 * Fixed problem with workaround for hanging cmd after data mode
 *
 * /main/ms-d/11   2006-11-14 16:59:52   wm36264
 * Tidy up more unused stuff
 *
 * /main/ms-d/10   2006-11-14 14:39:56   wm36264
 * Disposed unused CRilEventLog
 *
 * /main/ms-d/9   2006-11-13 11:57:51   wm36264
 * Read data mode only once
 *
 * /main/ms-d/8   2006-11-13 11:53:59   wm36264
 * Added workaround for failing 1st cmd after data mode
 *
 * /main/ms-d/7   2006-11-13 10:05:09   mp49210
 * New PIN2 handling in Quinn
 *
 * /main/ms-d/6   2006-11-10 12:50:03   wm36264
 * Simplyfied "Wait for Response" mechanism
 *
 * /main/ms-d/5   2006-11-02 11:09:26   wm36264
 * Fixed copy & paste bug in last modification
 *
 * /main/ms-d/4   2006-11-02 10:46:24   wm36264
 * Prepared correct port assignment to commands
 *
 * /main/ms-d/3   2006-11-01 15:10:24   wm36264
 * Added basic support for cmd modes (unfinished!!!)
 *
 * /main/ms-d/2   2006-10-30 12:31:23   wm36264
 * First basic port differentiation
 *
 * /main/ms-d/1   2006-10-25 16:52:38   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"

#define RESP_DATA_BUF 1024

#define NETWORK_USSD_TIMEOUT 20000      // Timeout for network initiated USSD commands

//
// Globals
//

// Command and Response Queues
CPriorityQueue<CCommand, COMMAND_QUEUE_SIZE>    g_CmdQ;
CQueue<CResponse, RESPONSE_QUEUE_SIZE>          g_RspQ;
CPriorityQueue<CCommand, COMMAND_QUEUE_SIZE>    g_NoopCmdQ;

extern BOOL g_fReleaseHeldCall;

DWORD g_dwWaitBeforeATCommand = 0;
extern BOOL g_fCallWaiting;
extern BOOL g_fEmergencyCall;

//////////////////////////////////////////////////////////////////////////////
//
// Internal functions
//
//////////////////////////////////////////////////////////////////////////////

//
//
//
static DWORD WINAPI RequeueThreadProc(LPVOID lpParameter)
{
    REQUEUE_THREAD_DATA* prtd = (REQUEUE_THREAD_DATA*)lpParameter;
    CRilHandle *pHandle = NULL;
    BOOL bIsSMSSendCmd = FALSE;

    (void)g_PowerManager.WaitCmdRequeue(prtd->dwDelay);

    if (prtd->pCmd != NULL)
    {
        if (prtd->pCmd->GetHandle() != NULL)
            pHandle = prtd->pCmd->GetHandle()->GetDevice();
    }
    if (pHandle != NULL && prtd->pCmd->GetAPIID() == APIID_SENDMSG && g_bChangeCNMIwhileSMSsend && !g_bDefaultCNMIstoreOnSIM)
        bIsSMSSendCmd = TRUE;
    if (bIsSMSSendCmd && pHandle->GetCurrentTechnology() == TEC_MODE_CDMA)
        bIsSMSSendCmd = FALSE;     // No special handling for CDMA-SMS

    if (bIsSMSSendCmd)
    {
        if (USE_STANDARD_CNMI_SETTINGS)
            (void)QueueInternalCmd(HCCMD_STANDARD, pHandle, "AT"SMS_CNMI_STORE_ON_SIM_3GPP"\r", CMDOPT_NONE, APIID_SENDMSGACKNOWLEDGEMENT, 2000, 0, 0);
        else
            (void)QueueInternalCmd(HCCMD_STANDARD, pHandle, "AT"SMS_CNMI_STORE_ON_SIM"\r", CMDOPT_NONE, APIID_SENDMSGACKNOWLEDGEMENT, 2000, 0, 0);
    }
    // Place the command into the command queue
    //    (this may block if the queue is full)
    if (!g_CmdQ.Put(prtd->pCmd, INFINITE))
    {
        delete prtd->pCmd;
    }
    prtd->pCmd = NULL;
    delete prtd;

    if (bIsSMSSendCmd)
    {
        (void)QueueInternalCmd(HCCMD_STANDARD, pHandle, "AT"SMS_CNMI_MANUAL_ACK"\r", CMDOPT_NONE, APIID_SENDMSGACKNOWLEDGEMENT, 2000, 0, 0);
    }
    return 0;
}

//
//
//
BOOL RequeueCmdWithDelay(CCommand* pCmd, DWORD dwDelay) {

    REQUEUE_THREAD_DATA* prtd = NULL;
    HANDLE hThread;
    CCommand* pCmdNew = NULL;
    DWORD dwThreadID;
    BOOL fRet = FALSE;

    EnterCriticalSection(&g_csClosing);

#ifdef RILDRV_CE6
// Visual Studio sees a possible memory leak through an exception
// which actually doesn't exists.
#pragma warning (push)
#pragma warning ( disable : 6211 )
#endif
    // Allocate parameter structure to be passed to the thread proc
    prtd = new REQUEUE_THREAD_DATA;
    if (!prtd)
    {
        goto Error;
    }
#ifdef RILDRV_CE6
#pragma warning (pop)
#endif

    // Set up a command to be sent
    pCmdNew = new CCommand;
    if (!pCmdNew || !pCmdNew->Init(pCmd, TRUE))
    {
        goto Error;
    }

    if (pCmdNew->FPlanB())
    {
        if (!pCmdNew->UsePlanB())
        {
            goto Error;
        }
    }

    if (pCmdNew->GetHandle())
    {
        if (!pCmdNew->GetHandle()->GetCmdList()->Add(pCmdNew))
        {
            goto Error;
        }
    }

    prtd->pCmd = pCmdNew;
    prtd->dwDelay = dwDelay;
    hThread = CreateThread(NULL, 0, RequeueThreadProc, (LPVOID)prtd, 0, &dwThreadID);
    if (!hThread)
    {
        goto Error;
    }
    (void)CloseHandle(hThread);
    fRet = TRUE;

    Error:
    if (!fRet)
    {
        delete prtd;
        delete pCmdNew;
    }

    LeaveCriticalSection(&g_csClosing);

    return fRet;
}

//
// Calculate the timing information for a command
//
static void CalculateCmdTiming(CRilHandle* const pRilDevice, const APIID apiid, DWORD dwInitTimeout, DWORD* pdwTimeout) {
    DWORD dwTimeout = dwInitTimeout;

    if (pRilDevice && (apiid != APIID_NONE) && !dwTimeout) {
      // Get the timing info about this API
      APIINFO apiiInfo;
      pRilDevice->GetAPIInfo(apiid, apiiInfo);
      dwTimeout = apiiInfo.dwTimeout;
    } else {
      // Get the default timing info
      if (!dwTimeout) {
        dwTimeout = g_TimeoutAPIDefault;
      }
    }

    // Extend the timeout to account for CTS polling (+CFUN=7)
    if (dwTimeout != INFINITE) {
        dwTimeout += 2000;
    }

    if (pdwTimeout) *pdwTimeout = dwTimeout;
}

//
// Place a command into the Command Queue
//
BOOL QueueCmdWithTimeout(HCCMD_MODE CmdMode, CRilInstanceHandle* const pHandle, const LPCSTR szCmd, const LPCSTR szCmdPart2, const DWORD dwOptions, const APIID apiid,
              const PFN_CMD_PARSE pfnParse, CNotificationData* const pnd, HRESULT& rhrCmdID, DWORD dwTimeout, DWORD dwRetries, DWORD dwRetryDelay,
              const PFN_CMD_ERROR pfnError, const LPVOID pParseParam, const CME_TRANSLATION *pCMET, const PFN_PRE_CMD_CHECK pfnPreCheck) {

    DWORD               dwUsedTimeout;
    CRilHandle*         pRilDevice = pHandle->GetDevice();
    CCommand*           pCmd = NULL;
    CNotificationData*  pndNotify = pnd;
    BOOL                fRet = FALSE;

    CalculateCmdTiming(pRilDevice, apiid, dwTimeout, &dwUsedTimeout);

    // Set up a command to be sent
    pCmd = new CCommand;
    if (!pCmd)
    {
        goto Error;
    }
    if (!pCmd->Init(pHandle, szCmd, szCmdPart2, dwOptions, CmdMode, dwUsedTimeout, pndNotify, pfnParse, dwRetries, dwRetryDelay, apiid, pfnError, pParseParam, pCMET, pfnPreCheck))
    {
        goto Error;
    }

    pndNotify = NULL;

    // Add this command to the handle's command list
    (void)pHandle->GetCmdList()->Add(pCmd);

    rhrCmdID = pCmd->GetID();

    if (dwOptions & CMDOPT_DIAL)
    {
        pHandle->GetDevice()->SetIgnoreNextNOCARRIER(FALSE);
    }

#ifdef USE_NOOP_QUEUE
    if (CmdMode == HCCMD_NOOP_PORT || pCmd->FNoOp())       // Handle all noop-commands in noop-queue
#else
    if (CmdMode == HCCMD_NOOP_PORT)               // Handle noop-commands in noop-queue, if explicitly sent to it
#endif
    {
        // Place the command into the noop-command queue
        if (!g_NoopCmdQ.Put(pCmd, INFINITE))
        {
            goto Error;
        }
    }
    else {
        // Place the command into the standard command queue
        //    (this may block if the queue is full)
        if (!g_CmdQ.Put(pCmd, INFINITE))
        {
            goto Error;
        }
    }

    if (pCmd->FHangup() || pCmd->FDial() || pCmd->FAnswer())
    {
        (void)SetEvent(g_hNewDialOrHangupEvent);
    }

    pCmd = NULL;
    fRet = TRUE;

    Error:
    if (!fRet)
    {
        delete pCmd;
        delete pndNotify;
    }
    return fRet;
}

//
// Place an internal multipartcommand into the command queue
//
BOOL QueueInternalMultipartCmd(HCCMD_MODE CmdMode, CRilHandle* const pRilDevice, const LPCSTR szCmd, const LPCSTR szCmd2, const DWORD dwOptions, const APIID apiid, DWORD dwTimeout, DWORD dwRetries, DWORD dwRetryDelay,
                      const PFN_CMD_PARSE pfnParse, const PFN_CMD_ERROR pfnError, const LPVOID pParseParam, CNotificationData* const pnd, const CME_TRANSLATION *pCMET) {
    DWORD               dwUsedTimeout;
    CCommand*           pCmd = NULL;
    CNotificationData*  pndNotify = pnd;
    BOOL                fRet = FALSE;

    CalculateCmdTiming(pRilDevice, apiid, dwTimeout, &dwUsedTimeout);

    // Set up a command to be sent
    pCmd = new CCommand;
    if (!pCmd)
    {
        goto Error;
    }

    if (!pCmd->Init(NULL, szCmd, szCmd2 , dwOptions, CmdMode, dwUsedTimeout, pndNotify, pfnParse, dwRetries, dwRetryDelay, apiid, pfnError, pParseParam, pCMET, NULL))
    {
        goto Error;
    }

    pndNotify = NULL;

    // Place the command into the command queue
    //    (this may block if the queue is full)
    if (!g_CmdQ.Put(pCmd, INFINITE))
    {
        goto Error;
    }

    pCmd = NULL;
    fRet = TRUE;

    Error:
    if (!fRet)
    {
        delete pCmd;
        delete pndNotify;
    }
    return fRet;
}

//
// Place an internal command into the command queue
//
BOOL QueueInternalCmd(HCCMD_MODE CmdMode, CRilHandle* const pRilDevice, const LPCSTR szCmd, const DWORD dwOptions, const APIID apiid, DWORD dwTimeout, DWORD dwRetries, DWORD dwRetryDelay,
                      const PFN_CMD_PARSE pfnParse, const PFN_CMD_ERROR pfnError, const LPVOID pParseParam, CNotificationData* const pnd, const CME_TRANSLATION *pCMET)
{
    return QueueInternalMultipartCmd(CmdMode, pRilDevice, szCmd, NULL, dwOptions, apiid, dwTimeout, dwRetries, dwRetryDelay, pfnParse, pfnError, pParseParam, pnd, pCMET);
}

//
// Parsing callback for blocking command queueing functions
//
static HRESULT ParseUnblockErr(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam, DWORD& dwCode) {
  QUEUE_BLOCKING_DATA*  pQBlockingData = (QUEUE_BLOCKING_DATA*)pParam;
  HRESULT               Ret = S_OK;

  if (pQBlockingData->pfnParseErr && szRsp) {
    Ret = pQBlockingData->pfnParseErr(szRsp, pBlob, cbBlob, pQBlockingData->pParam, dwCode);
  }
  if (dwCode != RIL_RESULT_OK && pBlob != NULL && cbBlob == sizeof(HRESULT)) {
    // Save original error code.
    pQBlockingData->hrError = *((HRESULT *)pBlob);
  }
  (void)SetEvent(pQBlockingData->hEvents[dwCode == RIL_RESULT_OK ? 0 : 1]);
  return Ret;
}

//
// Parsing callback for blocking command queueing functions
//
static HRESULT ParseUnblockOk(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam) {
  QUEUE_BLOCKING_DATA*  pQBlockingData = (QUEUE_BLOCKING_DATA*)pParam;
  HRESULT               Ret = S_OK;

  if (pQBlockingData->pfnParseOk) {
    Ret = pQBlockingData->pfnParseOk(szRsp, pBlob, cbBlob, pQBlockingData->pParam);
  }
  (void)SetEvent(pQBlockingData->hEvents[SUCCEEDED(Ret) ? 0 : 1]);
  return Ret;
}

//
// Place an internal multipart command into the command queue and block until the command has been executed
//
BOOL QueueInternalMultipartCmdBlocking(HCCMD_MODE CmdMode, CRilHandle* const pRilDevice, const LPCSTR szCmd, const LPCSTR szCmd2, const DWORD dwOptions, const APIID apiid, DWORD dwTimeout, DWORD dwRetries, DWORD dwRetryDelay,
                              const PFN_CMD_PARSE pfnParse, const PFN_CMD_ERROR pfnError, const LPVOID pParseParam, CNotificationData* const pnd, const CME_TRANSLATION *pCMET, HRESULT *phr)
{
    QUEUE_BLOCKING_DATA  QBlockingData;
    BOOL                 fRet = FALSE;

    // Store data passed to parsing functions
    QBlockingData.pfnParseOk = pfnParse;
    QBlockingData.pfnParseErr = pfnError;
    QBlockingData.pParam = pParseParam;
    QBlockingData.hEvents[0] = CreateEvent(NULL, TRUE, FALSE, NULL);
    QBlockingData.hEvents[1] = CreateEvent(NULL, TRUE, FALSE, NULL);
    QBlockingData.hrError = S_OK;
    if (!QBlockingData.hEvents[0] || !QBlockingData.hEvents[1]) {
      CloseHandle(QBlockingData.hEvents[0]);
      CloseHandle(QBlockingData.hEvents[1]);
      return FALSE;
    }

    // Queue the command
    if (QueueInternalMultipartCmd(CmdMode, pRilDevice, szCmd, szCmd2, dwOptions, apiid, dwTimeout, dwRetries, dwRetryDelay, ParseUnblockOk, ParseUnblockErr, &QBlockingData, pnd, pCMET)) {
      if (WaitForMultipleObjects(2, QBlockingData.hEvents, FALSE, INFINITE) == WAIT_OBJECT_0) {
        fRet = TRUE;
      }
    }

    // If required, return error code
    if (phr != NULL) *phr = QBlockingData.hrError;

    CloseHandle(QBlockingData.hEvents[0]);
    CloseHandle(QBlockingData.hEvents[1]);

    return fRet;
}

//
// Place an internal command into the command queue and block until the command has been executed
//
BOOL QueueInternalCmdBlocking(HCCMD_MODE CmdMode, CRilHandle* const pRilDevice, const LPCSTR szCmd, const DWORD dwOptions, const APIID apiid, DWORD dwTimeout, DWORD dwRetries, DWORD dwRetryDelay,
                              const PFN_CMD_PARSE pfnParse, const PFN_CMD_ERROR pfnError, const LPVOID pParseParam, CNotificationData* const pnd, const CME_TRANSLATION *pCMET, HRESULT *phr)
{
    return QueueInternalMultipartCmdBlocking(CmdMode, pRilDevice, szCmd, NULL, dwOptions, apiid, dwTimeout, dwRetries, dwRetryDelay, pfnParse, pfnError, pParseParam, pnd, pCMET, phr);
}

//
// Place a command into the Command Queue and block until the command has been executed
//
BOOL QueueCmdBlocking(HCCMD_MODE CmdMode, CRilInstanceHandle* const pHandle, const LPCSTR szCmd, const DWORD dwOptions, const APIID apiid, const PFN_CMD_PARSE pfnParse, CNotificationData* const pnd,
              HRESULT& rhrCmdID, DWORD dwTimeout, DWORD dwRetries, DWORD dwRetryDelay, const PFN_CMD_ERROR pfnError, const LPVOID pParseParam) {
    QUEUE_BLOCKING_DATA  QBlockingData;
    BOOL                 fRet = FALSE;

    // Check input parameter
    if (!pHandle) {
      return FALSE;
    }

    // Store data passed to parsing functions
    QBlockingData.pfnParseOk = pfnParse;
    QBlockingData.pfnParseErr = pfnError;
    QBlockingData.pParam = pParseParam;
    QBlockingData.hEvents[0] = CreateEvent(NULL, TRUE, FALSE, NULL);
    QBlockingData.hEvents[1] = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (!QBlockingData.hEvents[0] || !QBlockingData.hEvents[1]) {
      CloseHandle(QBlockingData.hEvents[0]);
      CloseHandle(QBlockingData.hEvents[1]);
      return FALSE;
    }

    // Queue the command
    if (QueueCmdWithTimeout(CmdMode, pHandle, szCmd, NULL, dwOptions, apiid, ParseUnblockOk, pnd, rhrCmdID, dwTimeout, dwRetries, dwRetryDelay, ParseUnblockErr, &QBlockingData, NULL, NULL)) {
      if (WaitForMultipleObjects(2, QBlockingData.hEvents, FALSE, INFINITE) == WAIT_OBJECT_0) {
        fRet = TRUE;
      }
    }

    CloseHandle(QBlockingData.hEvents[0]);
    CloseHandle(QBlockingData.hEvents[1]);

    return fRet;
}

//
// Place a command into the Command Queue
//
BOOL QueueCmd(HCCMD_MODE CmdMode, CRilInstanceHandle* const pHandle, const LPCSTR szCmd, const DWORD dwOptions, const APIID apiid,
              const PFN_CMD_PARSE pfnParse, CNotificationData* const pnd, HRESULT& rhrCmdID, const PFN_CMD_ERROR pfnError, const LPVOID pParseParam)
{
    return QueueCmdWithTimeout(CmdMode, pHandle, szCmd, NULL, dwOptions, apiid, pfnParse, pnd, rhrCmdID, 0, 0, 0, pfnError, pParseParam);
}

//
//
//
BOOL QueueCmdWithRetry(HCCMD_MODE CmdMode, CRilInstanceHandle* const pHandle, const LPCSTR szCmd, const DWORD dwOptions, const APIID apiid,
              const PFN_CMD_PARSE pfnParse, CNotificationData* const pnd, HRESULT& rhrCmdID, DWORD dwRetries, DWORD dwRetryDelay, const PFN_CMD_ERROR pfnError, const LPVOID pParseParam)
{
    return QueueCmdWithTimeout(CmdMode, pHandle, szCmd, NULL, dwOptions, apiid, pfnParse, pnd, rhrCmdID, 0, dwRetries, dwRetryDelay, pfnError, pParseParam);
}

//
// Place a multipart command into the Command Queue
//
BOOL QueueMultipartCmd(HCCMD_MODE CmdMode, CRilInstanceHandle* const pHandle, const LPCSTR szCmd, const LPCSTR szCmdPart2, const DWORD dwOptions, const APIID apiid,
              const PFN_CMD_PARSE pfnParse, CNotificationData* const pnd, HRESULT& rhrCmdID, const PFN_CMD_ERROR pfnError, const LPVOID pParseParam)
{
    return QueueCmdWithTimeout(CmdMode, pHandle, szCmd, szCmdPart2, dwOptions, apiid, pfnParse, pnd, rhrCmdID, 0, 0, 0, pfnError, pParseParam);
}

//
// Place a multipart command into the Command Queue
//
BOOL QueueMultipartCmdWithRetry(HCCMD_MODE CmdMode, CRilInstanceHandle* const pHandle, const LPCSTR szCmd, const LPCSTR szCmdPart2, const DWORD dwOptions, const APIID apiid,
              const PFN_CMD_PARSE pfnParse, CNotificationData* const pnd, HRESULT& rhrCmdID, const PFN_CMD_ERROR pfnError, const LPVOID pParseParam, DWORD dwRetries, DWORD dwRetryDelay)
{
    return QueueCmdWithTimeout(CmdMode, pHandle, szCmd, szCmdPart2, dwOptions, apiid, pfnParse, pnd, rhrCmdID, 0, dwRetries, dwRetryDelay, pfnError, pParseParam);
}

//
// Place a command into the Command Queue
//
BOOL QueueCmdCMET(HCCMD_MODE CmdMode, CRilInstanceHandle* const pHandle, const LPCSTR szCmd, const DWORD dwOptions, const APIID apiid,
              const PFN_CMD_PARSE pfnParse, CNotificationData* const pnd, HRESULT& rhrCmdID, const CME_TRANSLATION *pCMET,  DWORD dwRetries, DWORD dwRetryDelay, const LPVOID pParseParam)
{
    return QueueCmdWithTimeout(CmdMode, pHandle, szCmd, NULL, dwOptions, apiid, pfnParse, pnd, rhrCmdID, 0, dwRetries, dwRetryDelay, NULL, pParseParam, pCMET);
}

//
//
//
BOOL QueueRsp(CRilHandle* pRilDevice, CResponse*& rpRsp)
{
    BOOL fRet = FALSE;

    // Remember that we're not waiting for AT response anymore
    pRilDevice->StopWaitingForRsp();

    // Queue the command response
    if (!g_RspQ.Put(rpRsp, INFINITE))
    {
        goto Error;
    }
    rpRsp = NULL;

    fRet = TRUE;

    Error:
    return fRet;
}

//
//
//
DWORD CRilHandle::CommandThread(CCheckPoint* pCheckPoint)
{
    CCommand*      pCmd = NULL;
    COM_PORT_TYPE  PortType;
    HRESULT        hr;

    // Tell the main thread that we've reached the checkpoint
    pCheckPoint->Reached();

    for (;;)
    {
        DWORD dwTimeOut;

        // Make sure the module is prepared to handle the command before sending it (except for init commands)
        if (!WaitForCommandOrCancel())
        {
            delete pCmd;
            break;
        }

        // When the module is in the USSD answer mode, the AT command interface is blocked.
        // There is nothing else to do, than to wait until it is unblocked.
        dwTimeOut = 0;
        while (m_fUSSDAnswerMode && (dwTimeOut < NETWORK_USSD_TIMEOUT))
        {
            Sleep(100);
            dwTimeOut += 100;
        }
        // Still blocked, we cancel the mode the hard way.
        if (m_fUSSDAnswerMode)
        {
            CancelUSSDAnswerMode(DATA_PORT);
            CancelUSSDAnswerMode(URC_PORT);
        }

        // Get the next command from the Queue
        hr = g_CmdQ.Get(pCmd, INFINITE);
        if (RIL_E_CANCELLED == hr)
        {
            delete pCmd;
            UnlockRilCmd();
            break;
        }

        // If we failed initialization, ignore any non-init commands in the queue
        if (m_fFailedInit && !pCmd->FInit())
        {
            HRESULT dwError = RIL_E_RADIOFAILEDINIT;
            pCmd->SendResponse(RIL_RESULT_ERROR, &dwError, sizeof(HRESULT));
            delete pCmd;
            pCmd = NULL;
            UnlockRilCmd();
            continue;
        }

        // Determine the correct port for the command
        switch (pCmd->GetCmdMode())
        {
          case HCCMD_STANDARD:
            if (USE_URC_PORT_AS_DEFAULT)
                PortType = URC_PORT;    // Use URC port as default if the current radio requires it.
            else
                PortType = m_pComDevice[DATA_PORT]->FDataMode() ? URC_PORT : DATA_PORT;
            break;
          case HCCMD_DATAPORT_ONLY:
            if (m_pComDevice[DATA_PORT]->FDataMode()) {
              // Return an error, if we hava a data port only command when we are in data mode
              if (pCmd->GetCmd() == NULL) {
                RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: Command for data port while in data mode: NOOP\r\n")));
              }
              else {
                ExtRILRetailTraceZone(ZONE_ERROR, TEXT("Command for data port while in data mode: "), NULL, pCmd->GetCmd());
              }
              HRESULT dwError = RIL_E_CMDCANTBEACTIONED;
              pCmd->SendResponse(RIL_RESULT_ERROR, &dwError, sizeof(HRESULT));
              delete pCmd;
              pCmd = NULL;
              UnlockRilCmd();
              continue;
            }
            PortType = DATA_PORT;
            break;
          case HCCMD_URCPORT_ONLY:
            PortType = URC_PORT;
            break;
          default:
            RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: Unknown command mode\r\n")));
            delete pCmd;
            pCmd = NULL;
            UnlockRilCmd();
            continue;
        }

        // Send the command to comm port
        Sleep(g_dwWaitBeforeATCommand);
        if (!m_pComDevice[PortType]->SendRILCmdHandleRsp(this, pCmd))
        {
            // Don't give up and exit, just try again
            continue;
        }
    }

    return 0;
}

//
//
//
DWORD CRilHandle::ResponseThread(COM_PORT_TYPE PortType, CCheckPoint* pCheckPoint)
{
    char   szData[RESP_DATA_BUF];
    DWORD  dwRead;

    // Tell the main thread that we've reached the checkpoint
    pCheckPoint->Reached();

    for (;;)
    {
        // See if the thread needs to terminate
        dwRead = RESP_DATA_BUF;
        if (!WaitForResponseOrCancel(PortType, (PBYTE)szData, dwRead))
        {
            break;
        }

        if (!HandleRxData(PortType, szData, dwRead))
        {
            break;
        }
    }

    return 0;
}

//
//
//
DWORD CRilHandle::NoopCommandThread(CCheckPoint* pCheckPoint)
{
    CCommand*      pCmd = NULL;
    HRESULT        hr, hrPre;
    PFN_PRE_CMD_CHECK   pfnPreCheck;
    CResponse*  pRsp = NULL;

    // Tell the main thread that we've reached the checkpoint
    pCheckPoint->Reached();
    for (;;)
    {
        // Wait for next command (or cancel signal)
        if (!WaitForNoopCommandOrCancel())
        {
            break;
        }

        // Get the next command from the Queue
        hr = g_NoopCmdQ.Get(pCmd, INFINITE);
        if (RIL_E_CANCELLED == hr)
        {
            break;
        }
        if (!pCmd->FNoOp())
        {
            HRESULT dwError = RIL_E_SERVICEOPTIONTEMPORARILYOUTOFORDER;

            RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: NoopCommandThread() - None NOOP command detected\r\n")));
            pCmd->SendResponse(RIL_RESULT_ERROR, &dwError, sizeof(HRESULT));
            delete pCmd;
            pCmd = NULL;
            continue;
        }

        // If we failed initialization, ignore any non-init commands in the queue
        if (m_fFailedInit && !pCmd->FInit())
        {
            HRESULT hrError = RIL_E_RADIOFAILEDINIT;

            pCmd->SendResponse(RIL_RESULT_ERROR, &hrError, sizeof(HRESULT));
            delete pCmd;
            pCmd = NULL;
            continue;
        }

        pfnPreCheck = pCmd->GetPreCheckFunc();
        hrPre = (pfnPreCheck==NULL ? S_OK : pfnPreCheck(this, URC_PORT));
#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: NoopCommandThread() - Module powered off externally\r\n")));
            g_PowerManager.ForcePowerOff();
            if (!pCmd->CmdOptIsSet(CMDOPT_IGNORERADIOOFF)) {
                hr = RIL_E_RADIOOFF;
                // Send out cancelled response as a notification
                pCmd->SendResponse(RIL_RESULT_ERROR, &hr, sizeof(HRESULT));
                delete pCmd;
                pCmd = NULL;
                continue;
            }
        }
#endif

        // Allocate a new response
        pRsp = new CResponse(URC_PORT, this);
        if (!pRsp)
        {
            // Critically low on memory
            RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: No memory for new noop response!")));
            SignalCriticalError();
            continue;
        }

        // Create an OK response (since the command was a no-op, it can't fail)
        pRsp->MakeOK();
        // Unlikely case: pre-init failed.
        if (FAILED(hrPre))
            (void)pRsp->OKToError(hrPre);

        // Handle the response (can't fail on noop command)
        (void)HandleRsp(pCmd, pRsp);
    }

    if (pCmd != NULL) delete pCmd;
    return 0;
}

//
// Thread responsible for sending commands from the Command Queue to COM port
//
DWORD WINAPI CmdThreadProc(LPVOID lpParameter)
{
    QUEUE_THREAD_DATA* pData = (QUEUE_THREAD_DATA*)lpParameter;
    return pData->pRilDevice->CommandThread(pData->pCheckPoint);
}

//
// Thread responsible for reading responses from COM port into the Response Queue
//
DWORD WINAPI ReadThreadProc(LPVOID lpParameter)
{
    QUEUE_THREAD_DATA* pData = (QUEUE_THREAD_DATA*)lpParameter;

    // Switch the thread into higher priority (to guarantee that the module's in buffer doesn't get overflown)
    if (!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL))
    {
        return 0;
    }

    return pData->pRilDevice->ResponseThread(pData->PortType, pData->pCheckPoint);
}

//
// Thread responsible for handling the NOOP commands
//
DWORD WINAPI NoopCmdThreadProc(LPVOID lpParameter)
{
    QUEUE_THREAD_DATA* pData = (QUEUE_THREAD_DATA*)lpParameter;
    return pData->pRilDevice->NoopCommandThread(pData->pCheckPoint);
}

//
// Handle the received data
//
BOOL CRilHandle::HandleRxData(COM_PORT_TYPE PortType, const char *szData, DWORD dwRead)
{
    LPSTR szAppend = NULL;
    LPSTR szRemainder = NULL;
    UINT cbAppend;
    UINT cbRemainder;
    void* pBlob;
    UINT cbBlob;
    CResponse* pRsp = NULL;
    BOOL bSuccess=FALSE;

    if (!AppendReadBytes(PortType, szData, dwRead))
    {
        goto Exit;
    }

    while (GiveUpReadBytes(PortType, szAppend, cbAppend))
    {
        if (!cbAppend)
        {
            // Free memory occupied by szAppend
            delete[] szAppend;
            szAppend = NULL;
            break;
        }

        if (!pRsp)
        {
            // Allocate a new response, if we need it
            pRsp = new CResponse(PortType, this);
            if (!pRsp)
            {
                // Critically low on memory
                RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: No memory for new response!")));
                SignalCriticalError();
                goto Exit;
            }
        }

        if (m_fLastCommandTimedOut[PortType])
        {
            pRsp->SetPotentialBogusResponseFlag();
            m_fLastCommandTimedOut[PortType] = FALSE;
        }

#ifdef RILDRV_CE6
        if (!pRsp->AppendString(szAppend, cbAppend, const_cast<LPCSTR&>(szRemainder), cbRemainder))
#else
        if (!pRsp->AppendString(szAppend, cbAppend, szRemainder, cbRemainder))
#endif
        {
            // Critically low on memory
            RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: No memory for new response string!")));
            SignalCriticalError();
            goto Exit;
        }

        // Does this contain a complete response?
        if (!szRemainder)
        {
            // No -- break out of this loop and wait for more data to come

            // Move the data back to the Read Bytes buffer
            if (!InheritReadBytes(PortType, pRsp))
            {
                // Critically low on memory
                RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: No memory for new bytes to read!")));
                SignalCriticalError();
                goto Exit;
            }

            // Free memory occupied by szAppend
            delete[] szAppend;
            szAppend = NULL;
            cbAppend = 0;
            break;
        }
        else
        {
            if (pRsp->FUnrecognized())
            {
                // We couldn't recognize this response -- assume it was corrupted and throw it away
                delete pRsp;
                pRsp = NULL;
            }
            // Handle command results. A special case is "BUSY": usually it is an unsolicited response,
            // but if there is a command executed and this command is a dial command it is a response
            // to this command. This doesn't apply if the dial command is a SAT setup call command,
            // in that case it has to be handled as an unsolicited response.
            else if (!pRsp->FUnsolicited() || (RIL_RESULT_BUSY == pRsp->GetNotifyCode() && FWaitingForRsp() && m_pCurrCommand->FDial() && m_pCurrCommand->GetAPIID() != APIID_SENDSIMTOOLKITCMDRESPONSE))
            {
                // This is a command result -- is someone waiting for it?
                GetComDevice(PortType)->StopWaitDataMode();
                if (FWaitingForRsp())
                {
                    // No -- see if it's one of the exceptions, and if not, throw it away
                    if (RIL_RESULT_NOCARRIER == pRsp->GetNotifyCode() && m_pCurrCommand != NULL && !(m_pCurrCommand->FDial() || m_pCurrCommand->FAnswer()))
                    {
                        if(g_fEmergencyCall) {
                            g_fEmergencyCall = FALSE;
                            (void)g_GpsHandling.SwitchGpsStatePassive(FALSE);
                        }

                        (void)BroadcastDWORDBlobNotification(RIL_NOTIFY_DISCONNECT, RIL_DISCINIT_REMOTE);
                        if (IsVoiceCallDialing())
                        {
                            CloseHandle(CreateThread(NULL, 0, ReCheckCallStatusThreadProc, (LPVOID)this, 0, 0));
                            SetVoiceCallDialing(FALSE);
                        }
                        g_fCallWaiting = FALSE;
                        TestLastActiveCall(this);
                        g_fReleaseHeldCall = FALSE;
                        delete pRsp;
                        pRsp = NULL;
                    }
                    else if (pRsp->GetNotifyCode()==0) {
                        // This might happen, when an unused URC (e.g. RING on a non URC channel) shows
                        // up between a command and the response. In this case we simply discard the response.
                        RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: discarded response: %s\r\n"), VerboseString(TString(pRsp->GetData()))));
                        delete pRsp;
                        pRsp = NULL;
                    }
                    else
                    {
                        // Yes -- place it into the Response Q
                        if (!QueueRsp(this, pRsp))
                        {
                            goto Exit;
                        }
                    }
                }
                else
                {
                    // No -- see if it's one of the exceptions, and if not, throw it away
                    if (RIL_RESULT_NOCARRIER == pRsp->GetNotifyCode())
                    {
                        if (g_fEmergencyCall)
                        {
                            g_fEmergencyCall = FALSE;
                            (void)g_GpsHandling.SwitchGpsStatePassive(FALSE);
                        }

                        // To prevent multiple disconnect notifications we only report notifications received on the URC port.
                        // Exception: if the disconnect is caused be an ATH command on the COMMAND_PORT, the disconnect
                        // notifications had been already sent, so we have to ignore this one.
                        if (PortType == URC_PORT)
                        {
                            if (m_fIgnoreNextNOCARRIER)
                                m_fIgnoreNextNOCARRIER = FALSE;
                            else
                            {
                                (void)BroadcastDWORDBlobNotification(RIL_NOTIFY_DISCONNECT, RIL_DISCINIT_REMOTE);
                                if (IsVoiceCallDialing())
                                {
                                    CloseHandle(CreateThread(NULL, 0, ReCheckCallStatusThreadProc, (LPVOID)this, 0, 0));
                                    SetVoiceCallDialing(FALSE);
                                }
                            }
                        }
                        g_fCallWaiting = FALSE;
                        TestLastActiveCall(this);
                        g_fReleaseHeldCall = FALSE;
                    }
                    else if (RIL_RESULT_NODIALTONE== pRsp->GetNotifyCode())
                    {
                        (void)BroadcastDWORDBlobNotification(RIL_NOTIFY_DISCONNECT, RIL_DISCINIT_NETWORKERROR);
                        if (IsVoiceCallDialing())
                        {
                            CloseHandle(CreateThread(NULL, 0, ReCheckCallStatusThreadProc, (LPVOID)this, 0, 0));
                            SetVoiceCallDialing(FALSE);
                        }
                    }
                    delete pRsp;
                    pRsp = NULL;
                }
            }
            else
            {
                if (RIL_RESULT_BUSY == pRsp->GetNotifyCode())
                {
                    if (PortType == URC_PORT)
                    {
                        (void)BroadcastDWORDBlobNotification(RIL_NOTIFY_DISCONNECT, RIL_DISCINIT_BUSY);
                        if (IsVoiceCallDialing())
                        {
                            CloseHandle(CreateThread(NULL, 0, ReCheckCallStatusThreadProc, (LPVOID)this, 0, 0));
                            SetVoiceCallDialing(FALSE);
                        }

                    }
                }
                else
                {
                    // This is an unsolicited notification -- broadcast it to all clients
                    // (with the exception of CONNECT's received while in data mode, because they are effectively
                    //  internal to RIL/VSP)
                    if (!m_pComDevice[PortType]->FDataMode() || RIL_NOTIFY_CONNECT != pRsp->GetNotifyCode())
                    {
                        if (pRsp->GetNotifyCode() != 0)     // Filter all URCs without any message.
                        {
                            pRsp->GetBlob(pBlob, cbBlob);
                            (void)BroadcastRealBlobNotification(pRsp->GetNotifyCode(), pBlob, cbBlob);
                            pRsp->DeleteBlob();
                            pBlob = NULL;
                            cbBlob = 0;
                        }
                    }

                    // See if the last command caused us to enter (or return) to data mode
                    if (RIL_NOTIFY_CONNECT == pRsp->GetNotifyCode())
                    {
                        // Yes -- let the virtual serial driver know
                        if (m_pComDevice[PortType]->EnterDataMode())
                        {
                            // Place a response to command that caused connection into the Response Queue
                            //    (the response will later be changed to OK)
                            if (!QueueRsp(this, pRsp))
                            {
                                goto Exit;
                            }

                            // Wait until module drops out of data mode
                            if (!m_pComDevice[PortType]->WaitForCommandMode())
                            {
                                goto Exit;
                            }
                        }
                    }
                }
                delete pRsp;
                pRsp = NULL;
            }

            // Append the remaining data to the read bytes buffer
            if (cbRemainder && !AppendReadBytes(PortType, szRemainder, cbRemainder))
            {
                goto Exit;
            }
        }

        // Free memory occupied by szAppend
        delete[] szAppend;
        szAppend = NULL;
        szRemainder = NULL;
        cbAppend = 0;
    }

    bSuccess=TRUE;
Exit:
    if (szAppend) delete[] szAppend;
    if (pRsp) delete pRsp;
    return bSuccess;
}

