/*****************************************************************************
 * Copyright (C) Gemalto M2M GmbH
 *****************************************************************************
 * Project: IMC-RIL-Driver
 *****************************************************************************
 * Author : Wireless Modules RD SW A2
 *****************************************************************************
 * $Workfile: Y:/z_ms-d/src/working/Quinn/RilDrv/command.cpp $
 * $Revision: /main/ms-d/14 $
 *     $Date: 2013-05-21 11:44:39 $
 *   $Author: pmackent $
 *****************************************************************************
 * Target:
 *  Microsoft Windows Mobile 5.0
 *****************************************************************************
 * Description:
 *  Gemalto IMC RIL
 *****************************************************************************
 *
 * $Log: Y:/z_ms-d/src/working/Quinn/RilDrv/command.cpp$ 
 * 
 * /main/ms-d/14   2013-05-21 11:44:39   pmackent 
 * Don't dial CDMA voice calls, if there is no coverage (IPIS100087446)
 *
 * /main/ms-d/13   2013-02-26 15:07:30   pmackent
 * Adaptions for EHS5 1.290
 *
 * /main/ms-d/12   2012-12-05 10:14:53   pmackent
 * Removed unsed call of GetCurrentPermissions() which causes an error message in CE7
 *
 * /main/ms-d/11   2012-02-23 16:10:09   ctna0256
 * Special handling of NO CARRIER in Intel-AT-Set
 *
 * /main/ms-d/10   2010-11-01 08:13:22   ctna0256
 * Merge fix for exception in command handling from Classic RIL (IPIS100068178)
 *
 * /main/ms-d/9   2010-02-03 10:50:46   ctna0256
 * Introduction of WinCE6 support
 *
 * /main/ms-d/8   2008-10-23 11:32:24   ctna0256
 * New trace option (RIL proxy)
 *
 * /main/ms-d/7   2008-08-22 10:23:18   ctna0256
 * Set Cinterion copyright text
 *
 * /main/ms-d/6   2008-08-21 09:34:13   ctna0256
 * Prepared for new disclaimer handling
 *
 * /main/ms-d/5   2008-07-09 09:47:18   mp49210
 * New debug traces for IOCTL handling
 *
 * /main/ms-d/4   2008-06-06 15:26:35   mp49210
 * New command option Plan_B
 *
 * /main/ms-d/3   2008-04-03 16:13:04   mp49210
 * Don't repeat AT+CRSM=192 if SIM is not inserted
 *
 * /main/ms-d/2   2006-12-27 14:20:16   mp49210
 * Workaround for wrong CME-error at AT+CPBR
 *
 * /main/ms-d/1   2006-10-25 16:53:13   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"

//
// Command ctor
//
CCommand::CCommand() :
  m_pHandle(NULL),
  m_dwOptions(CMDOPT_NONE),
  m_szCmd(NULL),
  m_szCmdPart2(NULL),
  m_pCMET(NULL),
  m_hrCmdID(0),
  m_dwTimeout(0),
  m_pndNotify(NULL),
  m_pfnParseOk(NULL),
  m_pfnParseError(NULL),
  m_pfnPreCheck(NULL),
  m_pParseParam(NULL),
  m_dwRetriesOnError(0),
  m_dwRetryOnErrorDelay(0),
  m_dwThreadPermission(0),
  m_apiid(APIID_NONE),
  m_CmdMode(HCCMD_INVALID)
{
}


//
// Command dtor
//
CCommand::~CCommand()
{
    DWORD dwCode = RIL_RESULT_ERROR;
    void*  pDummyBlob;
    UINT   uiDummyBlob;

    // Ensure that the error callback has been called to unblock possibly waiting functions
    (void)CallResultHandler(dwCode, NULL, pDummyBlob, uiDummyBlob);

    delete[] m_szCmd;
    m_szCmd = NULL;
    delete[] m_szCmdPart2;
    m_szCmdPart2 = NULL;
    delete m_pndNotify;
    m_pndNotify = NULL;
    SYNCBLOCK(g_csClosing);
    if (m_pHandle) {
      (void)m_pHandle->GetCmdList()->Remove(this);
    }
}


//
// Set the command data
//
BOOL CCommand::Init(CRilInstanceHandle* const pHandle, const LPCSTR szCmd, const LPCSTR szCmdPart2, const DWORD dwOptions, const HCCMD_MODE CmdMode,
                    const DWORD dwTimeout, CNotificationData* const pnd, const PFN_CMD_PARSE pfnParse,
                    const DWORD dwRetriesOnError, const DWORD dwRetryOnErrorDelay, const APIID apiid,
                    const PFN_CMD_ERROR pfnError, const LPVOID pParseParam, const CME_TRANSLATION *pCMET,
                    const PFN_PRE_CMD_CHECK pfnPreCheck)
{
    UINT cbCmd;
    BOOL fRet = FALSE;

    if (szCmd) {
        cbCmd = strlen(szCmd) + 1;
        delete[] m_szCmd;
        m_szCmd = new char[cbCmd];
        if (!m_szCmd) {
            goto Error;
        }
        (void)strncpyz(m_szCmd, szCmd, cbCmd);
    }

    if (szCmdPart2) {
        cbCmd = strlen(szCmdPart2) + 1;
        delete[] m_szCmdPart2;
        m_szCmdPart2 = new char[cbCmd];
        if (!m_szCmdPart2) {
            goto Error;
        }
        (void)strncpyz(m_szCmdPart2, szCmdPart2, cbCmd);
    }
    m_pCMET = pCMET;

    m_pHandle = pHandle;
    if (m_pHandle) {
        m_hrCmdID = pHandle->GetDevice()->GetNextCmdID();
    }

    m_dwOptions = dwOptions;
    m_dwTimeout = dwTimeout;
    m_pfnParseOk = pfnParse;
    m_pfnParseError = pfnError;
    m_pParseParam = pParseParam;
    m_pfnPreCheck = pfnPreCheck;
    m_pndNotify = pnd;
    m_dwRetriesOnError = dwRetriesOnError;
    m_dwRetryOnErrorDelay = dwRetryOnErrorDelay;
#ifndef RILDRV_CE6
    m_dwThreadPermission = GetCurrentPermissions();
#endif
    m_apiid = apiid;
    m_CmdMode = CmdMode;
    fRet = TRUE;

Error:
    if (!fRet) {
        delete[] m_szCmd;
        m_szCmd = NULL;
    }
    return fRet;
}


//
//
//
BOOL CCommand::Init(CCommand* pCmd, BOOL bClone)
{
    BOOL fRet;

    fRet = Init(pCmd->m_pHandle, pCmd->m_szCmd, pCmd->m_szCmdPart2, pCmd->m_dwOptions, pCmd->m_CmdMode, pCmd->m_dwTimeout,
                pCmd->GiveUpNotificationData(), pCmd->m_pfnParseOk, pCmd->m_dwRetriesOnError, pCmd->m_dwRetryOnErrorDelay, pCmd->m_apiid,
                pCmd->m_pfnParseError, pCmd->m_pParseParam, pCmd->m_pCMET, pCmd->m_pfnPreCheck);

    if (bClone) {
        m_hrCmdID = pCmd->GetID();
    }

    // Delete callback pointer of original command to avoid calling them there
    pCmd->m_pfnParseOk = NULL;
    pCmd->m_pfnParseError = NULL;

    // Restore the original thread permission
    m_dwThreadPermission = pCmd->m_dwThreadPermission;

    return fRet;
}

//
//
//
BOOL CCommand::UsePlanB()
{
    UINT cbCmd;

    cbCmd = strlen(m_szCmdPart2)+1;
    ClrCmdOpt(CMDOPT_PLAN_B);
    delete[] m_szCmd;
    m_szCmd = new char[cbCmd];
    if (!m_szCmd) {
        return FALSE;
    }
    (void)strncpyz(m_szCmd, m_szCmdPart2, cbCmd);
    return TRUE;
}

//
//
//
HRESULT CCommand::CallResultHandler(DWORD& dwCode, LPCSTR strResult, void*& pBlob, UINT& uiBlob)
{
    HRESULT Ret = S_OK;

    if (ResultHandlerAvailable(dwCode==RIL_RESULT_OK ? TRUE : FALSE)) {
#ifndef RILDRV_CE6
      SetProcPermissions(m_dwThreadPermission);
#endif
      Ret = (dwCode==RIL_RESULT_OK ? m_pfnParseOk(strResult, pBlob, uiBlob, m_pParseParam) : m_pfnParseError(strResult, pBlob, uiBlob, m_pParseParam, dwCode));
      // Some special error codes must be used as return codes.
      if ((HRESULT)Ret == RIL_RESULT_NODIALTONE || (HRESULT)Ret == RIL_RESULT_NOCARRIER || (HRESULT)Ret == RIL_RESULT_NOANSWER || (HRESULT)Ret == RIL_RESULT_BUSY)
        dwCode = (DWORD)Ret;
    }

    // Delete callback pointer after calling them to ensure they're called only once
    m_pfnParseOk = NULL;
    m_pfnParseError = NULL;

    return Ret;
}


//
//
//
DWORD CCommand::GetPriority(void) const
{
    DWORD dwPriority;

    if (FPower()) {
        dwPriority = CMDPRI_CRITICAL;
    } else if (FInit()) {
        dwPriority = CMDPRI_HIGHEST;
    } else if (FUrc() || IsSMSAck()) {
        dwPriority = CMDPRI_HIGH;
    } else if (FDial() || FHangup()) {
        dwPriority = CMDPRI_NORMAL;
    } else if (FLowestPrio()) {
        dwPriority = CMDPRI_LOWEST;
    } else {
        dwPriority = CMDPRI_LOW;
    }
    return dwPriority;
}

void CCommand::SendResponse(const DWORD dwResponseCode, const void* const pBlob, const UINT cbBlob)
{
    // Are we supposed to ignore the command response?
    if (!FIgnoreRsp()) {
        // No -- send out the result as a notification
        SYNCBLOCK(g_csClosing);

        // Retrieve the owning handle
        CRilInstanceHandle* pHandle = GetHandle();

        if (pHandle && pHandle->FReadyForNotifications()) {
            if (dwResponseCode == RIL_RESULT_NOCARRIER && GetAPIID() == APIID_DIAL && NEED_CHECK_NO_CARRIER_REASON && cbBlob == 0) {
                // This "NO CARRIER" can mean a lot of things, but it is necessary
                // to call AT+CEER to find out more. To avoid a possible deadlock
                // This command cannot be called here. Therfore all durther action
                // will be done in a new thread.
                CHECK_NOCARRIER_THREAD_DATA *pData;

                pData = (CHECK_NOCARRIER_THREAD_DATA *)AllocBlob(sizeof(CHECK_NOCARRIER_THREAD_DATA));
                pData->hrID = GetID();
                pData->apiid = GetAPIID();
                pData->dwResponseCode = dwResponseCode;
                pData->pHandle = pHandle;

                CloseHandle(CreateThread(NULL, 0, CheckNoCarrierReasonThreadProc, (LPVOID)pData, 0, 0));
            }
            else {
                TraceIOCTLCallAsynchReturn(GetID(),  GetAPIID(), dwResponseCode, pBlob, cbBlob);

                // Send out the resposne to the owning handle
                pHandle->Notify(dwResponseCode, GetID(), pBlob, cbBlob);
            }

            // Remove this command from the owning handle's command list
            (void)pHandle->GetCmdList()->Remove(this);
            ClearHandle();
        }
    }
}

//
// Requeues self in the command queue if all the
// retry attempts have not been exhausted.
//
BOOL CCommand::RetryCommandOnError(void)
{
    if (m_dwRetriesOnError > 0)
    {
        m_dwRetriesOnError--;
        return RequeueCmdWithDelay(this, m_dwRetryOnErrorDelay);
    }
    else
    {
        return FALSE;
    }
}

HRESULT CCommand::TranslateErrorCode(HRESULT hrModule) const
{
    HRESULT hr = hrModule;

    if (m_pCMET != NULL)
    {
        UINT i = 0;

        while (m_pCMET[i].hrModuleErrorCode != NULL) {
            if (m_pCMET[i].hrModuleErrorCode == hrModule) {
                hr = m_pCMET[i].hrMicrosoftErrorCode;
                break;
            }
            i++;
        }
    }

    return hr;
}

