/*****************************************************************************
 * Copyright (C) Gemalto M2M GmbH
 *****************************************************************************
 * Project: 3G-RIL-Driver
 *****************************************************************************
 * Author : Wireless Modules PD SW 1
 *****************************************************************************
 * $Workfile: Y:/z_ms-d/src/working/Quinn/RilDrv/sim_access.cpp $
 * $Revision: /main/ms-d/13 $
 *     $Date: 2013-06-20 14:21:50 $
 *   $Author: pmackent $
 *****************************************************************************
 * Target:
 *  Microsoft Windows Mobile 6.x
 *****************************************************************************
 * Description:
 *  Cinterion 3G RIL
 *****************************************************************************
 *
 * $Log: Y:/z_ms-d/src/working/Quinn/RilDrv/sim_access.cpp$ 
 * 
 * /main/ms-d/13   2013-06-20 14:21:50   pmackent 
 * Select ADF before PIN counter querey
 *
 * /main/ms-d/12   2013-02-26 15:07:59   pmackent
 * Adaptions for EHS5 1.290
 *
 * /main/ms-d/11   2012-12-12 15:24:30   pmackent
 * Workarounds for bugs in PXS8 2.821
 *
 * /main/ms-d/10   2012-11-05 11:03:25   pmackent
 * Don't use AT+CRSM in CDMA-only modus.
 *
 * /main/ms-d/9   2012-04-26 16:26:18   pmackent
 * Use phonebook delete workaround for all modules
 *
 * /main/ms-d/8   2012-02-28 13:10:16   ctna0256
 * Ensure correct phonebook selection before deleting entry
 *
 * /main/ms-d/7   2012-02-27 15:29:28   ctna0256
 * Workaround for phonebook entry delete problem
 *
 * /main/ms-d/6   2012-02-22 13:27:21   ctna0256
 * Encapsulate use of Intel AT command set
 *
 * /main/ms-d/5   2012-02-14 14:58:06   ctna0256
 * Improved comments
 *
 * /main/ms-d/4   2012-02-09 16:44:15   ctna0256
 * Check for missed SMS with AT+CRSM on EHS5, small bug fixes
 *
 * /main/ms-d/3   2012-02-08 15:15:57   ctna0256
 * Add Intel-style SAT
 *
 * /main/ms-d/1   2012-02-03 13:58:22   ctna0256
 * Phonebook and SIM access now in separate files
 *
 *
*/

//////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2012 Gemalto M2M GmbH
//
// 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"
#include <simtkit.h>

// Some special SIM files, for detailed information refer to GSM 11.11.
#define SIM_EF_SPN      0x6F05
#define SIM_EF_ECC      0x6FB7
#define SIM_EF_ADN      0x6F3A
#define SIM_EF_PBR      0x4F30

// Special command to trigger filling of ECC cache
#define FILL_ECC_CACHE_CMD      0x1FFFF

// This variable holds the last detected SIM type (2G or 3G).
SIMTYPE g_nSIMType = SIMTYPE_UNKNOWN;

// Holds the file number of the EF_ADN file according to EF_PBR on 3G SIMs.
static DWORD g_dwEFADNPBR = 0;

//
// Restricted SIM commands
//
static const DWORD g_rgdwRestrictedSIMCmds[] =
{
    (DWORD)-1,     // 0
    176,    // RIL_SIMCMD_READBINARY
    178,    // RIL_SIMCMD_READRECORD
    192,    // RIL_SIMCMD_GETRESPONSE
    214,    // RIL_SIMCMD_UPDATEBINARY
    220,    // RIL_SIMCMD_UPDATERECORD
    242,    // RIL_SIMCMD_STATUS
};
#define NUM_RESTRICTEDSIMCMDS   (sizeof(g_rgdwRestrictedSIMCmds) / sizeof(DWORD))

// Cache needed for empty ECC recrod workaround.
#define ECC_RECORD_CACHE_SIZE   100
UINT g_uiECCRecordCacheSize = 0;
BYTE g_uiECCRecordCache[ECC_RECORD_CACHE_SIZE];

//
// Telephone number are are coded as BCD on SIM cards.
// Possible values: 0..9, *, #, p
char Nibble2Digit(BYTE bNibble)
{
    const char cBCD[] = {'*', '#', 'p', 'b', 'c'};

    if (bNibble > 14) return '\0';
    if (bNibble > 9) return cBCD[bNibble-10];
    return '0'+(char)bNibble;
}

//
// Determines whether a file ID is an elementary SIM file or not
//
BOOL IsElementarySimFile(DWORD dwFileID)
{
    BOOL fRetVal = TRUE;
    BYTE bIdentifier;

    // Only the low WORD should be set
    if (dwFileID & 0xffff0000)
    {
        fRetVal = FALSE;
    }
    else
    {
        bIdentifier = (BYTE) ((dwFileID & 0xff00) >> 8);
        if ((bIdentifier != 0x2f) && (bIdentifier != 0x4f) && (bIdentifier != 0x6f))
        {
            // Bogus!  It's not an elementary file
            fRetVal = FALSE;
        }
    }

    return fRetVal;
}

//
//
//
static HRESULT ParseSendSimCmd(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    UINT i;
    UINT nValue = 0;
    UINT cbResponse = 0;
    BYTE* pbResponse = NULL;
    BYTE* pbResponseWalk;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    // Parse "<prefix>+CSIM: <length>,"
    if (!ParseRspPrefix(szRsp, szRsp)                  ||
        !MatchStringBeginning(szRsp, "+CSIM: ", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp)         ||
        !MatchStringBeginning(szRsp, ",\"", szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    if (0 < nValue) {
        // Allocate the storage
        cbResponse = nValue / 2;
        pbResponse = (BYTE *)AllocBlob(cbResponse);
        if (!pbResponse) {
            hr = E_OUTOFMEMORY;
            goto Error;
        }

        pbResponseWalk = pbResponse;
        for (i = 0; i < cbResponse; i++) {
            if (!*szRsp || !*(szRsp + 1)) {
                hr = E_FAIL;
                goto Error;
            }
            *pbResponseWalk = SemiByteCharsToByte(*szRsp, *(szRsp + 1));
            pbResponseWalk++;
            szRsp += 2;
        }

        // Parse "<postfix>"
        if (!MatchStringBeginning(szRsp, "\"", szRsp) || !ParseRspPostfix(szRsp, szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
    }
    else {
        // Special case where the response was 0 length.
        // Handle it by trashing the rest of the response.
        if (!MatchStringAnywhere(szRsp, "\r\n", szRsp))
        {
            hr = E_FAIL;
            goto Error;
        }
    }

    pBlob = (void*)pbResponse;
    cbBlob = cbResponse;

Error:
    if (FAILED(hr)) {
        (void)FreeBlob(pbResponse);
    }
    return hr;
}

//
//
//
HRESULT RILDrv_SendSimCmd(DWORD dwParam, const BYTE* lpbCommand, DWORD dwSize)
{
    LPSTR szCmd = NULL;
    char szPrefix[MAX_ATCMD_LEN];
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);

    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    if (g_PowerManager.IsRadioOff())
    {
        // If the radio is off it makes no sense to send this command.
        // Simply return "radio off" error message.
        if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_GETSIMRECORDSTATUS, ParseForceFailureResponse, NULL, hr, NULL, (LPVOID)RIL_E_RADIOOFF))
        {
            hr = E_FAIL;
        }
        goto Error;
    }
    if (pHandle->GetDevice()->GetMultiModeSetting() == MULTIMODE_CONFIG_CDMA)
    {
        // In CDMA-only mode there isn't a SIM, so it makes no sense to send a AT+CRSM command.
        if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_GETSIMRECORDSTATUS, ParseForceFailureResponse, NULL, hr, NULL, (LPVOID)RIL_E_INVALIDINDEX))
        {
            hr = E_FAIL;
        }
        goto Error;
    }

    if (pHandle->GetDevice()->GetCurrentSIMType() == SIMTYPE_UNKNOWN)
    {
        // If the SIM type is not known yet, the AT+CSIM command must not be send to the module. For
        // this reason the RIL returns a SIM busy error right away.
        // If the SIM is not inserted, the AT-command will fail, but doing no harm. So the RIL can
        // send it.
        if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_GETSIMRECORDSTATUS, ParseForceFailureResponse, NULL, hr, NULL, (LPVOID)RIL_E_SIMBUSY))
        {
            hr = E_FAIL;
        }
        goto Error;
    }

    // Add "AT+CSIM=<length>," to prefix string
    (void)_snprintfz(szPrefix, MAX_ATCMD_LEN, "AT+CSIM=%u,", dwSize * 2);

    // Add "<prefix>\"<command>\"<CR>"
    // NOTE: we take ownerswhip of allocated szCmd
    if (!ComposeCmdWithByteArray(szPrefix, lpbCommand, dwSize, "\r", szCmd)) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }

    if (!QueueCmd(HCCMD_STANDARD, pHandle, szCmd, CMDOPT_LOWESTPRIO, APIID_SENDSIMCMD, ParseSendSimCmd, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    delete szCmd;
    return hr;
}

//
//
//
static HRESULT PreInitSIMCmd(CRilHandle * pHandle, COM_PORT_TYPE /*iPort*/)
{
    HRESULT hr = S_OK;

    // If the SIM type is still unknown, SIM commands may lead into the radio
    // powering off, so it is safer to return a SIM busy error than executing the
    // command.
    if(pHandle->GetCurrentSIMType() == SIMTYPE_UNKNOWN)
        hr = RIL_E_SIMBUSY;

    //Since there is no SIM in CDMA, don't return a "SIM not inserted" error.
    if (pHandle->GetMultiModeSetting() == MULTIMODE_CONFIG_CDMA)
        hr = RIL_E_INVALIDINDEX;

    if (g_PowerManager.IsRadioOff())
        hr = RIL_E_RADIOOFF;

    return hr;
}

//
// GetEfAdnFileNumber()
// This function parses the response of an EF_PBR file to find out about the
// record number of the EF_ADN file.
//
static void GetEfAdnFileNumber(RILSIMRESPONSE* pSIMResonse)
{
    UINT nIdx = 0;

    // This is the EF_PBR file of a 3G SIM. There must be the number of the
    // EF_ADN file encoded here. First check if this a valid response.

    if (pSIMResonse->dwParams != (RIL_PARAM_SR_RESPONSE|RIL_PARAM_SR_STATUSWORD1|RIL_PARAM_SR_STATUSWORD2))
        return;
    if (pSIMResonse->dwStatusWord1 != 144 || pSIMResonse->dwStatusWord2 != 0 || pSIMResonse->cbSize == sizeof(RILSIMRESPONSE))
        return;

    while (nIdx < pSIMResonse->cbSize - sizeof(RILSIMRESPONSE)) {
        // The ADN file might be in the A8 section of the PBR file.
        if (pSIMResonse->pbResponse[nIdx] == 0xA8) {
            UINT nLen = pSIMResonse->pbResponse[nIdx] +2;
            UINT iIdx2 = 0;

            while (iIdx2 < nLen) {
                if (pSIMResonse->pbResponse[nIdx+2+iIdx2] == 0xC0) {
                    // Store file number of EF_PBR file, end search
                    g_dwEFADNPBR = (pSIMResonse->pbResponse[nIdx+2+iIdx2+2]<<8) + pSIMResonse->pbResponse[nIdx+2+iIdx2+3];
                    break;
                }
                else {
                    // Not the right tag, skip over it.
                    iIdx2 += 1 + pSIMResonse->pbResponse[nIdx+2+iIdx2+1];
                }
            }
            // Record evaluated, can skip rest now.
            break;
        }
        else {
            // Not the right tag, skip over it.
            nIdx += 1 + pSIMResonse->pbResponse[nIdx+1];
        }
    }
}

//
// FillECCCache()
// If it happens, that the ECC SIM file is read, the RIL uses this oportunity
// to fill the cache with ECC numbers. This cache is used to find out, if the
// number currently been dialed is an emergency number.
//
static void FillECCCache(RILSIMRESPONSE* pSIMResonse)
{
    // If not all parameters are present, there is no valid ECC field.
    if (pSIMResonse->dwParams != (RIL_PARAM_SR_RESPONSE|RIL_PARAM_SR_STATUSWORD1|RIL_PARAM_SR_STATUSWORD2))
        return;
    if (pSIMResonse->dwStatusWord1 != 144 || pSIMResonse->dwStatusWord2 != 0 || pSIMResonse->cbSize == sizeof(RILSIMRESPONSE))
        return;

    if (g_nSIMType == SIMTYPE_3G_SIM)
    {
        char szEMNo[MAX_EMERGENCYNO];
        INT i;

        memset(szEMNo, 0, MAX_EMERGENCYNO);
        for (i=0; i<3; i++)
        {
            szEMNo[2*i] = Nibble2Digit(pSIMResonse->pbResponse[i] & 0xF);
            szEMNo[(2*i)+1] = Nibble2Digit(pSIMResonse->pbResponse[i] >> 4);
        }
        if (szEMNo[0] != 0)
        {
            BOOL bFound = FALSE;

            for (i=0; i<(INT)g_uiECCCacheSize; i++)
            {
                if (strncmp(szEMNo, g_szECCCache[i], MAX_EMERGENCYNO)==0)
                {
                    bFound = TRUE;
                    break;
                }
            }
            if (!bFound && g_uiECCCacheSize < ECC_CACHE_SIZE)
            {
                (void)strncpyz(g_szECCCache[g_uiECCCacheSize], szEMNo, MAX_EMERGENCYNO);
                g_uiECCCacheSize++;
            }
        }
    }
    else
    {
        g_uiECCCacheSize = 0;       // Delete old content of the cache;
        memset(&g_szECCCache[0][0], 0, ECC_CACHE_SIZE*MAX_EMERGENCYNO);

        while ((g_uiECCCacheSize * 3) < (pSIMResonse->cbSize - sizeof(RILSIMRESPONSE)) && g_uiECCCacheSize < ECC_CACHE_SIZE)
        {
            INT i;

            for (i=0; i<3; i++)
            {
                g_szECCCache[g_uiECCCacheSize][2*i] = Nibble2Digit(pSIMResonse->pbResponse[(g_uiECCCacheSize*3)+i] & 0xF);
                g_szECCCache[g_uiECCCacheSize][(2*i)+1] = Nibble2Digit(pSIMResonse->pbResponse[(g_uiECCCacheSize*3)+i] >> 4);
            }
            g_uiECCCacheSize++;
        }
    }
    return;
}

//
// The GSM 11.11 and the TS 102.221 sometimes define different SIM files
// for the same purpose. In case an UICC is used, the SIM file according
// to TS 102.221 must be used. Since the Microsoft MMI only asks for the
// files according to GSM 11.11, the RIL must route the requests to the
// correct UICC SIM file.
//
static DWORD GetUICCFileID(CRilHandle *pHandle, DWORD dwOrgFileID)
{
    DWORD dwRealFileID = dwOrgFileID;

    if (pHandle->GetCurrentSIMType() == SIMTYPE_3G_SIM)  // UICC in use?
    {
        // Translate EF_LP (GSM 11.11) to EF_PL (TS 102.221)
        if (dwOrgFileID == 0x6F05)
            dwRealFileID = 0x2F05;
    }
    return dwRealFileID;
}

//
// NeedSIMPath()
// Often it is necessary to use the whole path of a SIM file in order to access
// it. Unfortunately the Microsoft RIL API doesn't have the concept of such a
// SIM path. As a solution it is possible to specifies SIM pathes for given SIM
// SIM file IDs via registry. See RIL Documentation for details.
//
// Parameters:
// nSIMType [in]: SIM type (2G or 3G)
// dwSIMFile [in]: SIM EF file number
// szPath [out]: string buffer where the SIM path will be placed
// uiPathLen [in]: length of buffer for szPath
//
// Return value: TRUE, if a SIM path is needed (path is stored in szPath)
//
BOOL NeedSIMPath(SIMTYPE nSIMType, DWORD dwSIMFile, char *szPath, UINT uiPathLen)
{
    BOOL bNeedPath = FALSE;
    WCHAR wszPath[MAX_PATH];
    WCHAR wszRegKey[MAX_PATH];
    UINT iWildCards = 0;

    // The HC25 Rel2 doesn't support SIM pathes.
    if (!SUPPORTS_CRSM_PATH) return FALSE;

    memset(szPath, 0, uiPathLen);
    memset(wszPath, 0, sizeof(wszRegKey));


    while (!bNeedPath && iWildCards < 3) {
        switch (iWildCards) {
            case 0:
                // Search in registry, if there is an entry for the given SIM file.
                if (nSIMType == SIMTYPE_3G_SIM)
                    (void)_snwprintfz(wszRegKey, MAX_PATH, L"3G_%04X", dwSIMFile);
                else
                    (void)_snwprintfz(wszRegKey, MAX_PATH, L"2G_%04X", dwSIMFile);
                break;
            case 1:
                // If no entry for this specific SIM file can be found, try with the
                // wildcard 'X' for the last digit.
                if (nSIMType == SIMTYPE_3G_SIM)
                    (void)_snwprintfz(wszRegKey, MAX_PATH, L"3G_%03XX", dwSIMFile>>4);
                else
                    (void)_snwprintfz(wszRegKey, MAX_PATH, L"2G_%03XX", dwSIMFile>>4);
                break;
            case 2:
                // If an entry still cannot be found, try with two
                // wildcards 'XX' for the last digits.
                if (nSIMType == SIMTYPE_3G_SIM)
                    (void)_snwprintfz(wszRegKey, MAX_PATH, L"3G_%02XXX", dwSIMFile>>8);
                else
                    (void)_snwprintfz(wszRegKey, MAX_PATH, L"2G_%02XXX", dwSIMFile>>8);
                break;
        }

        // Try to read the registry key for this SIM path..
        if (GetRegistrySZ(HKEY_LOCAL_MACHINE, TEXT("Software\\Microsoft\\RIL\\SIMPath"), wszRegKey, wszPath, MAX_PATH)) {
            if (wcslen(wszPath) > 0) {
                (void)_snprintfz(szPath, uiPathLen, "%S", wszPath);
                bNeedPath = TRUE;
            }
        }
        iWildCards ++;
    }

    return bNeedPath;
}

//
// IsDForMFfile()
// Returns TRUE, if the file ID been given belongs the Master File or a known
// Directory File. The RIL doesn't actually access the SIM here.
//
// Parameter:
// dwFileID: File ID of the SIM file to be tested.
//
// Return value: TRUE, if dwFileID is of type MF or DF,
//
static BOOL IsDForMFfile(DWORD dwFileID)
{
    BOOL bIsDF = FALSE;

    // IDs of MF and recognized DFs.
    if (dwFileID == 0x3F00 || dwFileID == 0x7F10 || dwFileID == 0x7F20 || dwFileID == 0x5F50 || dwFileID == 0x5F3A)
        bIsDF = TRUE;

    return bIsDF;
}

//
// IsForbiddenSIMFile()
// In some situations it is better not to access certain SIM files. Per registry
// key it is possible to specify one or more SIM files the RIL must not directly
// access via AT+CRSM. See also RIL Documentation.
//
// Parameter:
// dwSIMFile: the SIM file ID to be tested.
//
// Return value: TRUE, if the access to this SIM file is forbidden.
//
BOOL IsForbiddenSIMFile(DWORD dwSIMFile)
{
    WCHAR wszForbiddenSIMFile[MAX_ATCMD_LEN];

    memset(wszForbiddenSIMFile, 0, sizeof(wszForbiddenSIMFile));

    // Read registry key
    if (GetRegistrySZ(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("ForbiddenSIMFiles"), wszForbiddenSIMFile, MAX_ATCMD_LEN)) {
        WCHAR *wszToken;

        // The decimal SIM file numbers are separated with commas.
        wszToken = wcstok(wszForbiddenSIMFile, L",");
        while (wszToken != NULL)
        {
            if (dwSIMFile == (DWORD)_wtoi(wszToken))
                return TRUE;
            wszToken = wcstok(NULL, L",");
        }
    }
    return FALSE;
}

//
//
//
HRESULT ParseSendRestrictedSimCmd(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    UINT i;
    UINT nSW1;
    UINT nSW2;
    LPSTR szResponseString = NULL;
    UINT cbResponseString;
    UINT cbStruct;
    BYTE* pbResponseWalk;
    LPSTR pchResponseStringWalk;
    RILSIMRESPONSE* prsr = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    // Parse "<prefix>+CRSM: <sw1>,<sw2>"
    if (!ParseRspPrefix(szRsp, szRsp)                  ||
        !MatchStringBeginning(szRsp, "+CRSM: ", szRsp) ||
        !ParseUInt(szRsp, TRUE, nSW1, szRsp)           ||
        !MatchStringBeginning(szRsp, ",", szRsp)       ||
        !ParseUInt(szRsp, TRUE, nSW2, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Parse ","
    if (!MatchStringBeginning(szRsp, ",", szRsp)) {
        // No response data present
        cbStruct = sizeof(RILSIMRESPONSE);
    } else {
        // Parse "<response>"
        // NOTE: we take ownerswhip of allocated szResponseString
        if (!ParseUnlimitedString(szRsp, szResponseString, cbResponseString, szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        cbStruct = sizeof(RILSIMRESPONSE) + (cbResponseString - 1) / 2;
    }

    // Allocate the structure of needed size
    prsr = (RILSIMRESPONSE*)AllocBlob(cbStruct);
    if (!prsr) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prsr, 0x00, cbStruct);
    prsr->dwParams = (RIL_PARAM_SR_STATUSWORD1 | RIL_PARAM_SR_STATUSWORD2);

    // Decode the reponse data, if present
    if (sizeof(RILSIMRESPONSE) < cbStruct) {
        pbResponseWalk = prsr->pbResponse;
        pchResponseStringWalk = szResponseString;
        for (i = 0; i < cbStruct - sizeof(RILSIMRESPONSE); i++) {
            *pbResponseWalk = SemiByteCharsToByte(*pchResponseStringWalk, *(pchResponseStringWalk + 1));
            pbResponseWalk++;
            pchResponseStringWalk += 2;
        }
        prsr->dwParams |= RIL_PARAM_SR_RESPONSE;
    }

    prsr->cbSize = cbStruct;
    prsr->dwStatusWord1 = nSW1;
    prsr->dwStatusWord2 = nSW2;

    // Parse "<postfix>"
    if (!ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    pBlob = (void*)prsr;
    cbBlob = cbStruct;

    if ((DWORD)pParam == FILL_ECC_CACHE_CMD)
        FillECCCache(prsr);
    if ((DWORD)pParam == SIM_EF_PBR && g_nSIMType == SIMTYPE_3G_SIM) {
        // This is the EF_PBR file which contains the number of the EF_ADN file
        // on 3G SIM cards.
        GetEfAdnFileNumber(prsr);
    }

    if ((DWORD)pParam == SIM_EF_ECC)
    {
        // Bug in Microsft's MMI: if a SIM record in the ECC field (3G only) start with FF, all followinf emergency
        // numbers are ignored. Workaround: if such an record shows up, the RIL repeats the last record with
        // started with something else than FF.
        if (prsr->dwParams == (RIL_PARAM_SR_RESPONSE|RIL_PARAM_SR_STATUSWORD1|RIL_PARAM_SR_STATUSWORD2) && g_nSIMType == SIMTYPE_3G_SIM
            &&  prsr->dwStatusWord1 == 144 && prsr->dwStatusWord2 == 0 && prsr->cbSize > sizeof(RILSIMRESPONSE))
        {
            if (prsr->pbResponse[0] == 0xFF)
            {
                if (cbStruct == g_uiECCRecordCacheSize)
                    memcpy(prsr, g_uiECCRecordCache, cbStruct);
            }
            else {
                if (cbStruct < ECC_RECORD_CACHE_SIZE)
                {
                    g_uiECCRecordCacheSize = cbStruct;
                    memcpy(g_uiECCRecordCache, prsr, cbStruct);
                }
            }
        }
    }

Error:
    if (FAILED(hr)) {
        (void)FreeBlob(prsr);
    }
    delete[] szResponseString;
    return hr;
}

//
//
//
static HRESULT ParseSendRestrictedSimCmdCSIM(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    UINT nSW1;
    UINT nSW2;
    UINT i;
    UINT cbStruct;
    UINT nValue = 0;
    UINT cbResponse = 0;
    BYTE* pbResponse = NULL;
    BYTE* pbResponseWalk;
    HRESULT hr = S_OK;
    RILSIMRESPONSE* prsr = NULL;

    pBlob = NULL;
    cbBlob = 0;

    // Parse "<prefix>+CSIM: <length>,"
    if (!ParseRspPrefix(szRsp, szRsp)                  ||
        !MatchStringBeginning(szRsp, "+CSIM: ", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp)         ||
        !MatchStringBeginning(szRsp, ",\"", szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    if (nValue < 4) {
        // The response must contain at least 2 bytes. If not, we must return with an error.
        hr = E_FAIL;
        goto Error;
    }

    cbStruct = sizeof(RILSIMRESPONSE);
    if (nValue > 4) {
        // Allocate the storage
        cbResponse = nValue / 2;
        cbResponse = cbResponse - 2;
        pbResponse = (BYTE *)AllocBlob(cbResponse);
        if (!pbResponse) {
            hr = E_OUTOFMEMORY;
            goto Error;
        }

        cbStruct = sizeof(RILSIMRESPONSE) + cbResponse;

        pbResponseWalk = pbResponse;
        for (i = 0; i < cbResponse; i++) {
            if (!*szRsp || !*(szRsp + 1)) {
                hr = E_FAIL;
                goto Error;
            }
            *pbResponseWalk = SemiByteCharsToByte(*szRsp, *(szRsp + 1));
            pbResponseWalk++;
            szRsp += 2;
        }
    }

    nSW1 = SemiByteCharsToByte(*szRsp, *(szRsp + 1));
    szRsp += 2;

    nSW2 = SemiByteCharsToByte(*szRsp, *(szRsp + 1));
    szRsp += 2;

    // Parse "<postfix>"
    if (!MatchStringBeginning(szRsp, "\"", szRsp) || !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    else {
        // Special case where the response was 0 length.
        // Handle it by trashing the rest of the response.
        if (!MatchStringAnywhere(szRsp, "\r\n", szRsp))
        {
            hr = E_FAIL;
            goto Error;
        }
    }

    // Allocate the structure of needed size
    prsr = (RILSIMRESPONSE*)AllocBlob(cbStruct);
    if (!prsr) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prsr, 0x00, cbStruct);
    prsr->dwParams = (RIL_PARAM_SR_STATUSWORD1 | RIL_PARAM_SR_STATUSWORD2);

    if (cbResponse > 0 && pbResponse != NULL) {
        memcpy(prsr->pbResponse, pbResponse, cbResponse);
        prsr->dwParams |= RIL_PARAM_SR_RESPONSE;
    }

    prsr->cbSize = cbStruct;
    prsr->dwStatusWord1 = nSW1;
    prsr->dwStatusWord2 = nSW2;

    pBlob = (void*)prsr;
    cbBlob = cbStruct;

    if ((DWORD)pParam == SIM_EF_ECC)
        FillECCCache(prsr);
    if ((DWORD)pParam == SIM_EF_PBR && g_nSIMType == SIMTYPE_3G_SIM) {
        // This is the EF_PBR file which contains the number of the EF_ADN file
        // on 3G SIM cards.
        GetEfAdnFileNumber(prsr);
    }

Error:
    if (FAILED(hr)) {
        (void)FreeBlob(prsr);
    }
    (void)FreeBlob(pbResponse);
    return hr;
}

//
//
//
static HRESULT ParseSendRestrictedSimCmdCSIMStatusGSMSIM(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    UINT nSW1;
    UINT nSW2;
    UINT i;
    UINT cbStruct;
    UINT nValue = 0;
    UINT cbResponse = 0;
    BYTE* pbResponse = NULL;
    BYTE* pbResponseWalk;
    HRESULT hr = S_OK;
    RILSIMRESPONSE* prsr = NULL;

    pBlob = NULL;
    cbBlob = 0;

    // Parse "<prefix>+CSIM: <length>,"
    if (!ParseRspPrefix(szRsp, szRsp)                  ||
        !MatchStringBeginning(szRsp, "+CSIM: ", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp)         ||
        !MatchStringBeginning(szRsp, ",\"", szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    if (nValue < 4) {
        // The response must contain at least 2 bytes. If not, we must return with an error.
        hr = E_FAIL;
        goto Error;
    }

    cbStruct = sizeof(RILSIMRESPONSE);
    if (nValue > 4) {
        // Allocate the storage
        cbResponse = nValue / 2;
        cbResponse = cbResponse - 2;
        pbResponse = (BYTE *)AllocBlob(cbResponse);
        if (!pbResponse) {
            hr = E_OUTOFMEMORY;
            goto Error;
        }

        cbStruct = sizeof(RILSIMRESPONSE) + cbResponse;

        pbResponseWalk = pbResponse;
        for (i = 0; i < cbResponse; i++) {
            if (!*szRsp || !*(szRsp + 1)) {
                hr = E_FAIL;
                goto Error;
            }
            *pbResponseWalk = SemiByteCharsToByte(*szRsp, *(szRsp + 1));
            pbResponseWalk++;
            szRsp += 2;
        }
    }

    nSW1 = SemiByteCharsToByte(*szRsp, *(szRsp + 1));
    szRsp += 2;

    nSW2 = SemiByteCharsToByte(*szRsp, *(szRsp + 1));
    szRsp += 2;

    // Parse "<postfix>"
    if (!MatchStringBeginning(szRsp, "\"", szRsp) || !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    else {
        // Special case where the response was 0 length.
        // Handle it by trashing the rest of the response.
        if (!MatchStringAnywhere(szRsp, "\r\n", szRsp))
        {
            hr = E_FAIL;
            goto Error;
        }
    }

    // Allocate the structure of needed size
    prsr = (RILSIMRESPONSE*)AllocBlob(cbStruct);
    if (!prsr) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prsr, 0x00, cbStruct);
    prsr->dwParams = (RIL_PARAM_SR_STATUSWORD1 | RIL_PARAM_SR_STATUSWORD2);

    if (cbResponse > 0 && pbResponse != NULL) {
        memcpy(prsr->pbResponse, pbResponse, cbResponse);
        prsr->dwParams |= RIL_PARAM_SR_RESPONSE;
    }

    prsr->cbSize = cbStruct;
    prsr->dwStatusWord1 = nSW1;
    prsr->dwStatusWord2 = nSW2;

    pBlob = (void*)prsr;
    cbBlob = cbStruct;

    if ((DWORD)pParam == SIM_EF_ECC)
        FillECCCache(prsr);
    if ((DWORD)pParam == SIM_EF_PBR && g_nSIMType == SIMTYPE_3G_SIM) {
        // This is the EF_PBR file which contains the number of the EF_ADN file
        // on 3G SIM cards.
        GetEfAdnFileNumber(prsr);
    }

Error:
    if (FAILED(hr)) {
        (void)FreeBlob(prsr);
    }
    (void)FreeBlob(pbResponse);
    return hr;
}

//
//
//
static HRESULT ParseSendRestrictedSimCmdCSIMStatusUSIM(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    UINT nPINCounter = 0;
    UINT nPUKCounter = 0;
    UINT nPIN2Counter = 0;
    UINT nPUK2Counter = 0;
    HRESULT hr = S_OK;
    RILSIMRESPONSE* prsr = NULL;
    UINT cbStruct;

    pBlob = NULL;
    cbBlob = 0;

    cbStruct = sizeof(RILSIMRESPONSE) + 23;

    // Parse "<prefix>+CSIM: <counter>"
    if (!ParseRspPrefix(szRsp, szRsp)                  ||
        !MatchStringBeginning(szRsp, "+CSIM: 4,\"", szRsp) ||
        !ParseHexUInt(szRsp, TRUE, nPINCounter, szRsp) ||
        !MatchStringBeginning(szRsp, "\"", szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    if (!ParseRspPrefix(szRsp, szRsp)                  ||
        !MatchStringBeginning(szRsp, "+CSIM: 4,\"", szRsp) ||
        !ParseHexUInt(szRsp, TRUE, nPUKCounter, szRsp) ||
        !MatchStringBeginning(szRsp, "\"", szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Parse "<prefix>+CSIM: <counter>"
    if (!ParseRspPrefix(szRsp, szRsp)                  ||
        !MatchStringBeginning(szRsp, "+CSIM: 4,\"", szRsp) ||
        !ParseHexUInt(szRsp, TRUE, nPIN2Counter, szRsp) ||
        !MatchStringBeginning(szRsp, "\"", szRsp)) {
        nPIN2Counter = 0x63C3;       // Use PIN2 default value, CSIM failed
    }

    if (!ParseRspPrefix(szRsp, szRsp)                  ||
        !MatchStringBeginning(szRsp, "+CSIM: 4,\"", szRsp) ||
        !ParseHexUInt(szRsp, TRUE, nPUK2Counter, szRsp) ||
        !MatchStringBeginning(szRsp, "\"", szRsp)) {
        nPUK2Counter = 0x63CA;       // Use PUK2 default value, CSIM failed
    }

    // Parse "<postfix>"
    if (!ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Allocate the structure of needed size
    prsr = (RILSIMRESPONSE*)AllocBlob(cbStruct);
    if (!prsr) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prsr, 0x00, cbStruct);
    prsr->dwParams = (RIL_PARAM_SR_STATUSWORD1 | RIL_PARAM_SR_STATUSWORD2 | RIL_PARAM_SR_RESPONSE);

    // We fake a response to AT+CRSM=242 using the values from AT+CSIM responses.
    prsr->pbResponse[18] = (BYTE)(0x80|(nPINCounter&0xF));
    prsr->pbResponse[19] = (BYTE)(0x80|(nPUKCounter&0xF));
    prsr->pbResponse[20] = (BYTE)(0x80|(nPIN2Counter&0xF));
    prsr->pbResponse[21] = (BYTE)(0x80|(nPUK2Counter&0xF));
    prsr->cbSize = cbStruct;
    prsr->dwStatusWord1 = 144;
    prsr->dwStatusWord2 = 0;

    pBlob = (void*)prsr;
    cbBlob = cbStruct;

Error:
    if (FAILED(hr)) {
        (void)FreeBlob(prsr);
    }
    return hr;
}


//
//
//
HRESULT RILDrv_SendRestrictedSimCmd(DWORD dwParam, DWORD dwCommand, const RILSIMCMDPARAMETERS* lpParameters,
                                    const BYTE* lpbData, DWORD dwSize)
{
    LPSTR szCmd = NULL;
    char szPrefix[MAX_ATCMD_LEN];
    char szPath[MAX_PATH];
    LPSTR szPrefixWalk = szPrefix;
    UINT cbDataSizeToUse = 0;
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);

    if (!pHandle || !lpParameters) {
        hr = E_FAIL;
        goto Error;
    }

    if (g_PowerManager.IsRadioOff())
    {
        // If the radio is off it makes no sense to send this command.
        // Simply return "radio off" error message.
        if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_GETSIMRECORDSTATUS, ParseForceFailureResponse, NULL, hr, NULL, (LPVOID)RIL_E_RADIOOFF))
        {
            hr = E_FAIL;
        }
        goto Error;
    }
    if (pHandle->GetDevice()->GetMultiModeSetting() == MULTIMODE_CONFIG_CDMA)
    {
        // In CDMA-only mode there isn't a SIM, so it makes no sense to send a AT+CRSM command.
        if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_GETSIMRECORDSTATUS, ParseForceFailureResponse, NULL, hr, NULL, (LPVOID)RIL_E_INVALIDINDEX))
        {
            hr = E_FAIL;
        }
        goto Error;
    }

    if (pHandle->GetDevice()->GetCurrentSIMType() == SIMTYPE_UNKNOWN)
    {
        // If the SIM type is not known yet, the AT+CRSM command must not be send to the module. For
        // this reason the RIL returns a SIM busy error right away.
        // If the SIM is not inserted, the AT-command will fail, but doing no harm. So the RIL can
        // send it.
        if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_GETSIMRECORDSTATUS, ParseForceFailureResponse, NULL, hr, NULL, (LPVOID)RIL_E_SIMBUSY))
        {
            hr = E_FAIL;
        }
        // In order to know the SIM status next time, trigger a PIN check.
        (void)QueueInternalCmd(HCCMD_STANDARD, pHandle->GetDevice(), "AT+CPIN?\r", CMDOPT_NONE, APIID_GETPHONELOCKEDSTATE, 0, 0, 0, ParseGetPhoneLockedState, NULL, (LPVOID)pHandle->GetDevice());
        goto Error;
    }

    if (lpParameters && (lpParameters->dwParams & RIL_PARAM_SCP_FILEID))
    {
        if (IsForbiddenSIMFile(lpParameters->dwFileID))
        {
            // Some SIM files must not be accessed with AT+CRSM. In this case the RIL return
            // invalid index as error instead of executing the AT command.
            if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_GETSIMRECORDSTATUS, ParseForceFailureResponse, NULL, hr, NULL, (LPVOID)RIL_E_INVALIDINDEX))
            {
                hr = E_FAIL;
            }
            goto Error;
        }
    }

    if ((g_dwSIMLockedState == RIL_LOCKEDSTATE_SIM_PIN || g_dwSIMLockedState == RIL_LOCKEDSTATE_SIM_PUK || pHandle->GetDevice()->GetCurrentSIMType() == SIMTYPE_3G_SIM) && dwCommand == RIL_SIMCMD_STATUS) {
        // If the user has not yet entered the PIN, the MMI wants to know how many tries
        // are left by using the STATUS command. This doesn't work with AT+CRSM=242,
        // so we have to simulate this by using AT+CSIM command.
        // When the PIN is entered and the current SIM is an 3G-SIM, the MMI is most
        // probably in the SIM enable/disable dialog, so it also wants to know the PIN
        // counter. Since in the case the AT+CRSM=242 command doesn't work, the
        // AT+CSIM workaround is also needed here.
        if (pHandle->GetDevice()->GetCurrentSIMType() == SIMTYPE_3G_SIM) {
            // Select ADF first.
            (void)QueueInternalCmd(HCCMD_STANDARD, pHandle->GetDevice(), "AT+CSIM=14,\"00A4000C027FFF\"\r", CMDOPT_NONE, APIID_SENDRESTRICTEDSIMCMD, 0, 0, 0);
            // AT+CSIM commands for USIMs according to ETSI 102.221.
            if (!QueueCmd(HCCMD_STANDARD, pHandle, "AT+CSIM=10,\"0020000100\";+CSIM=10,\"002C000100\";+CSIM=10,\"0020008100\";+CSIM=10,\"002C008100\"\r", CMDOPT_NONE, APIID_SENDRESTRICTEDSIMCMD, ParseSendRestrictedSimCmdCSIMStatusUSIM, NULL, hr)) {
                hr = E_FAIL;
                goto Error;
            }
        }
        else {
            // AT+CSIM command for GSM-SIMs according to GSM 11.11.
            if (!QueueCmd(HCCMD_STANDARD, pHandle, "AT+CSIM=10,\"A0F2000016\"\r", CMDOPT_NONE, APIID_SENDRESTRICTEDSIMCMD, ParseSendRestrictedSimCmdCSIMStatusGSMSIM, NULL, hr)) {
                hr = E_FAIL;
                goto Error;
            }
        }
    }
    else
    {
        DWORD dwFile;

        // Add "AT+CRSM=<command>" to prefix string
        if (dwCommand >= NUM_RESTRICTEDSIMCMDS) {
            hr = E_INVALIDARG;
            goto Error;
        }
        (void)_snprintfz(szPrefixWalk, MAX_ATCMD_LEN - (szPrefixWalk - szPrefix), "AT+CRSM=%u", g_rgdwRestrictedSIMCmds[dwCommand]);
        szPrefixWalk = strchr(szPrefixWalk, '\0');  // NO_TYPO: 27

        if (lpParameters && (lpParameters->dwParams & RIL_PARAM_SCP_FILEID)) {
            // Add ",<fileid>" to prefix string
            (void)_snprintfz(szPrefixWalk, MAX_ATCMD_LEN - (szPrefixWalk - szPrefix), ",%u", GetUICCFileID(pHandle->GetDevice(), lpParameters->dwFileID));
            szPrefixWalk = strchr(szPrefixWalk, '\0');  // NO_TYPO: 27

            if ((lpParameters->dwParams & RIL_PARAM_SCP_PARAM1) &&
                (lpParameters->dwParams & RIL_PARAM_SCP_PARAM2) &&
                (lpParameters->dwParams & RIL_PARAM_SCP_PARAM3)) {
                // Add ",<p1>,<p2>,<p3>" to prefix string
                (void)_snprintfz(szPrefixWalk, MAX_ATCMD_LEN - (szPrefixWalk - szPrefix), ",%u,%u,%u",
                                 lpParameters->dwParameter1, lpParameters->dwParameter2, lpParameters->dwParameter3);

                // If there's data, add a comma before that data
                if (dwSize)
                {
                    (void)strncatz(szPrefix, ",\"", MAX_ATCMD_LEN);
                    cbDataSizeToUse = dwSize;
                }
            }
        }

        // Add "<prefix>\"<data>\"<CR>"
        // NOTE: we take ownerswhip of allocated szCmd
        if (!ComposeCmdWithByteArray(szPrefix, lpbData, cbDataSizeToUse, cbDataSizeToUse?"\"\r":"\r", szCmd)) {
            hr = E_OUTOFMEMORY;
            goto Error;
        }

        // Add <pathID>, if needed.
        if (lpParameters && NeedSIMPath(pHandle->GetDevice()->GetCurrentSIMType(), lpParameters->dwFileID, szPath, MAX_PATH))
        {
            szPrefixWalk = strchr(szCmd, '\r');
            if (dwSize == 0)
            {
                (void)_snprintfz(szPrefixWalk, MAX_ATCMD_LEN, ",");
                szPrefixWalk = strchr(szPrefixWalk, '\0');
            }
            (void)_snprintfz(szPrefixWalk, MAX_ATCMD_LEN - (szPrefixWalk - szCmd), ",\"%s\"\r", szPath);
        }

        // Special case: read ECC from 3G SIM. When reading the 1st record, the ECC cache must be deleted.
        if (lpParameters && pHandle->GetDevice()->GetCurrentSIMType() == SIMTYPE_3G_SIM)
        {
            if (lpParameters->dwFileID == SIM_EF_ECC && dwCommand == RIL_SIMCMD_READRECORD && lpParameters->dwParameter1 == 1)
                g_uiECCRecordCacheSize = 0;
        }

        if (pHandle->GetDevice()->GetEons()->MustReadIMSIbeforeReadingSIM())        // Read-IMSI-first workaround needed?
            (void)QueueInternalCmd(HCCMD_STANDARD, pHandle->GetDevice(), "AT+CRSM=192,28423,0,0,15\r", CMDOPT_LOWESTPRIO, APIID_GETSIMRECORDSTATUS, 0, 0, 0);

        g_nSIMType = pHandle->GetDevice()->GetCurrentSIMType();
        dwFile = GetUICCFileID(pHandle->GetDevice(), lpParameters->dwFileID);
        if (!QueueCmd(HCCMD_STANDARD, pHandle, szCmd, CMDOPT_LOWESTPRIO, APIID_SENDRESTRICTEDSIMCMD, ParseSendRestrictedSimCmd, NULL, hr, NULL, (LPVOID)(dwFile))) {
            hr = E_FAIL;
            goto Error;
        }
        if (NEED_DETECT_PBR_DELETE  && dwCommand == RIL_SIMCMD_UPDATERECORD && dwSize > 0 && lpbData != NULL && lpParameters != NULL) {
            BOOL fAllFF = TRUE;
            BOOL fIsEF_ADN = FALSE;
            SIMTYPE nSimType = pHandle->GetDevice()->GetCurrentSIMType();
            UINT i;

            // Problem with the EHS5: if the MMI tries to delete a phonebook entry
            // it might use the AT+CRSM command rather than the normal delete
            // phonebook API. This is a problem for the EHS5, because it is not
            // able to have its phonebook cache in sync with the SIM card.

            // 2G SIM: ADN is in a fixed file
            if (lpParameters->dwFileID == SIM_EF_ADN && nSimType == SIMTYPE_2G_SIM)
                fIsEF_ADN = TRUE;
            // 3G SIM: The EF_PBR (already read) contains the location of EF_ADN
            if (lpParameters->dwFileID == g_dwEFADNPBR && nSimType == SIMTYPE_3G_SIM)
                fIsEF_ADN = TRUE;

            // An UPDATE_RECORD command is a potentional delete phonebook
            // access. If this is the case, the complete data set will be filled
            // with FF.
            if (fIsEF_ADN) {
                for (i = 0; i < dwSize; i++) {
                    if (lpbData[i] != 0xFF) {
                        fAllFF = FALSE;
                        break;
                    }
                }
            }

            if (fIsEF_ADN && fAllFF && lpParameters->dwParameter1 > 0) {
                BOOL fPhonebookReady = TRUE;

                // If the SIM phonebook is currently not selected, selct it now.
                if (g_dwCurrentPhoneBook != RIL_PBLOC_SIMPHONEBOOK)
                    fPhonebookReady = QueueInternalCmdBlocking(HCCMD_STANDARD, pHandle->GetDevice(), "AT+CPBS=\"SM\"\r", CMDOPT_NONE, APIID_SETPHONEBOOKOPTIONS, 0, 0, 0);

                if (fPhonebookReady) {
                    // This is an actual delete phonebook entry command, so additionally
                    // call the AT+CPBW command.
                    (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CPBW=%d\r", lpParameters->dwParameter1);
                    (void)QueueInternalCmd(HCCMD_STANDARD, pHandle->GetDevice(), szCmd, CMDOPT_NONE, APIID_DELETEPHONEBOOKENTRY, 0, 0, 0);
                    // If the current phonebook hasn't been the SIM phonebook,
                    // switch back.
                    if (g_dwCurrentPhoneBook != RIL_PBLOC_SIMPHONEBOOK && g_dwCurrentPhoneBook !=  RIL_PBLOC_UNKNOWN && g_dwCurrentPhoneBook < NUM_PBLOCS) {
                        (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CPBS=\"%s\"\r", g_rgszPBLocations[g_dwCurrentPhoneBook]);
                        (void)QueueInternalCmd(HCCMD_STANDARD, pHandle->GetDevice(), szCmd, CMDOPT_NONE, APIID_SETPHONEBOOKOPTIONS, 0, 0, 0);
                    }
                }
            }
        }
    }
Error:
    delete szCmd;
    return hr;
}


//
//
//
HRESULT ParseGetSimRecordStatus(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    HRESULT hr = S_OK;
    RILSIMRESPONSE* prsr = NULL;
    RILSIMRECORDSTATUS *prsrs = NULL;
    UINT cbStruct = sizeof(RILSIMRECORDSTATUS);
    DWORD dwFileID;
    DWORD dwTotalSize;
    LPCSTR szDummy;
    CRilHandle *pHandle;

    pBlob = NULL;
    cbBlob = 0;
    pHandle = (CRilHandle *)pParam;
    if (pHandle == NULL) {
        hr = E_FAIL;
        goto Error;
    }

    // First fill in the RILSIMRESPONSE structure
    void *pBlobTmp=NULL;
    UINT cbBlobTmp=0;

    if (MatchStringAnywhere(szRsp, "+CSIM", szDummy))
        hr = ParseSendRestrictedSimCmdCSIM(szRsp, pBlobTmp, cbBlobTmp, NULL);
    else
        hr = ParseSendRestrictedSimCmd(szRsp, pBlobTmp, cbBlobTmp, NULL);
    prsr = (RILSIMRESPONSE *)pBlobTmp;
    if (FAILED(hr)) {
        goto Error;
    }

    // Now we need to munge this to be a RILSIMRECORDSTATUS structure instead
    prsrs = (RILSIMRECORDSTATUS*)AllocBlob(cbStruct);
    if (!prsrs) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prsrs, 0x00, cbStruct);

    // Was there an error of some sort?
    hr = DetermineSimResponseError(prsr->dwStatusWord1, prsr->dwStatusWord2);
    if (FAILED(hr)) {
        goto Error;
    }

    // USIM detected, so try to handle as USIM formated response,
    if (pHandle->GetCurrentSIMType() == SIMTYPE_3G_SIM)
    {
        UINT uiSize = prsr->cbSize - sizeof(RILSIMRESPONSE);
        UINT uiPointer = 2;

        // The response has to be present and at least 4 bytes long
        if ((!(prsr->dwParams & RIL_PARAM_SR_RESPONSE)) || (uiSize < 4))
        {
            hr = RIL_E_INVALIDINDEX;    // Invalid index indicates, that this SIM file either doesn't exists or is not accessible.
            goto Error;
        }

        prsrs->cbSize = cbStruct;
        prsrs->dwParams = RIL_PARAM_SRS_RECORDTYPE;
        prsrs->dwRecordType = RIL_SIMRECORDTYPE_UNKNOWN;

        while (uiPointer+1 < uiSize)
        {
            // incomplete tag? end loop.
            if (uiPointer+prsr->pbResponse[uiPointer+1] > uiSize)
                break;

            switch (prsr->pbResponse[uiPointer])
            {
                case 0x80:
                    {
                        if (prsrs->dwRecordType != RIL_SIMRECORDTYPE_UNKNOWN && prsrs->dwRecordType != RIL_SIMRECORDTYPE_LINEAR)
                        {
                            prsrs->dwParams |= RIL_PARAM_SRS_SIZE;
                            prsrs->dwSize = prsr->pbResponse[uiPointer+2]*256 + prsr->pbResponse[uiPointer+3];
                        }
                    }
                    break;

                case 0x82:
                    {
                        switch (prsr->pbResponse[uiPointer+2] & 0x7)
                        {
                            case 1:
                                prsrs->dwRecordType = RIL_SIMRECORDTYPE_TRANSPARENT;
                                break;
                            case 2:
                                prsrs->dwRecordType = RIL_SIMRECORDTYPE_LINEAR;
                                prsrs->dwParams |= RIL_PARAM_SRS_SIZE|RIL_PARAM_SRS_ITEMCOUNT;
                                prsrs->dwItemCount = prsr->pbResponse[uiPointer+6];
                                prsrs->dwSize = prsr->pbResponse[uiPointer+5] + (prsr->pbResponse[uiPointer+4] * 256);
                                break;
                            case 6:
                                prsrs->dwRecordType = RIL_SIMRECORDTYPE_CYCLIC;
                                break;
                        }
                    }
                    break;

                case 0x83:
                    dwFileID = prsr->pbResponse[uiPointer+2]*256 + prsr->pbResponse[uiPointer+3];
                    break;
            }
            uiPointer += (2+prsr->pbResponse[uiPointer+1]);
        }
    }

    // 2G-SIM detected, decode in 2G-format.
    // Also try to decode in 2G format if the SIM is supposed to be a 3G SIM, but the NEED_RECHECK_2G3G_SIM
    // workaround is needed.
    if ((pHandle->GetCurrentSIMType() != SIMTYPE_3G_SIM) ||
        (NEED_RECHECK_2G3G_SIM && prsrs->dwParams == RIL_PARAM_SRS_RECORDTYPE &&  prsrs->dwRecordType == RIL_SIMRECORDTYPE_UNKNOWN))
    {
        // The response has to be present and at least 7 bytes long
        if ((!(prsr->dwParams & RIL_PARAM_SR_RESPONSE)) || (prsr->cbSize - sizeof(RILSIMRESPONSE) < 7))
        {
            hr = RIL_E_INVALIDINDEX;    // Invalid index indicates, that this SIM file either doesn't exists or is not accessible.
            goto Error;
        }

        // The file ID should be in bytes 5 and 6 -- in either case, we need at least 7 bytes
        dwFileID = (prsr->pbResponse[4] << 8) | prsr->pbResponse[5];
        prsrs->cbSize = cbStruct;
        prsrs->dwParams = (RIL_PARAM_SRS_SIZE | RIL_PARAM_SRS_RECORDTYPE);
        prsrs->dwRecordType = RIL_SIMRECORDTYPE_UNKNOWN;
        if (IsElementarySimFile(dwFileID))
        {
            // Need at least 14 bytes for an elementary file
            if (prsr->cbSize - sizeof(RILSIMRESPONSE) < 14)
            {
                hr = E_FAIL;
                goto Error;
            }

            // Byte 14 tells us the file type
            switch (prsr->pbResponse[13])
            {
                case 0x00:
                case 0x02: // This is what OEM1 gives us, but it's not in 11.11!
                    prsrs->dwRecordType = RIL_SIMRECORDTYPE_TRANSPARENT;
                    break;
                case 0x01:
                    prsrs->dwRecordType = RIL_SIMRECORDTYPE_LINEAR;
                    break;
                case 0x03:
                    prsrs->dwRecordType = RIL_SIMRECORDTYPE_CYCLIC;
                    break;
                default:
                    break;
            }

            // Set more fields based on the file type
            dwTotalSize = (prsr->pbResponse[2] << 8) | prsr->pbResponse[3];
            if (prsrs->dwRecordType == RIL_SIMRECORDTYPE_TRANSPARENT)
            {
                // Size is bytes 3 and 4
                prsrs->dwParams |= RIL_PARAM_SRS_SIZE;
                prsrs->dwSize = dwTotalSize;
            }
            else if (prsrs->dwRecordType != RIL_SIMRECORDTYPE_UNKNOWN)
            {
                // Make sure we have a fifteenth byte in this case
                if (prsr->cbSize - sizeof(RILSIMRESPONSE) < 15)
                {
                    hr = E_FAIL;
                    goto Error;
                }

                prsrs->dwParams |= (RIL_PARAM_SRS_SIZE | RIL_PARAM_SRS_ITEMCOUNT);
                prsrs->dwSize = prsr->pbResponse[14];
                prsrs->dwItemCount = dwTotalSize / prsrs->dwSize;
            }
        }
        else
        {
            // We only set the file type, which is determined by byte 7
            switch (prsr->pbResponse[6])
            {
                case 0x01:
                    prsrs->dwRecordType = RIL_SIMRECORDTYPE_MASTER;
                    break;
                case 0x02:
                    prsrs->dwRecordType = RIL_SIMRECORDTYPE_DEDICATED;
                    break;
                default:
                    break;
            }
        }
        if ((NEED_RECHECK_2G3G_SIM && prsrs->dwRecordType != RIL_SIMRECORDTYPE_UNKNOWN && pHandle->GetCurrentSIMType() == SIMTYPE_3G_SIM)) {
             RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: Changing SIM type from 3G to 2G.\r\n")));
             pHandle->SetCurrentSIMType(SIMTYPE_2G_SIM);
        }
    }

    pBlob = (void*)prsrs;
    cbBlob = cbStruct;

Error:
    if (FAILED(hr)) {
        (void)FreeBlob(prsrs);
    }
    (void)FreeBlob(prsr);
    return hr;
}

const CME_TRANSLATION g_CMET_CRSM[] = {{RIL_E_OPNOTALLOWED,RIL_E_SIMNOTINSERTED},{NULL,NULL}};

//
//
//
static HRESULT ParseGetSimRecordStatusError(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/, DWORD& dwCode)
{
    LPCSTR szDummy;
    HRESULT hr = S_OK;

    // If there is no response string, the radio is currently off.
    if (szRsp == NULL)
        return RIL_E_RADIOOFF;

    // 'ERROR' doesn't indicate a real error.
    if (MatchStringAnywhere(szRsp, "ERROR\r", szDummy))
    {
        RILSIMRECORDSTATUS *prsrs = NULL;
        UINT cbStruct = sizeof(RILSIMRECORDSTATUS);

        // Delete old blob with error message..
        if (pBlob != NULL)
            (void)FreeBlob(pBlob);

        // Create a new blob indicating the SIM record type 'dedicated'.
        prsrs = (RILSIMRECORDSTATUS*)AllocBlob(cbStruct);
        if (!prsrs) {
            hr = E_OUTOFMEMORY;
            goto Error;
        }
        memset(prsrs, 0x00, cbStruct);

        prsrs->cbSize = cbStruct;
        prsrs->dwParams = (RIL_PARAM_SRS_SIZE | RIL_PARAM_SRS_RECORDTYPE);
        prsrs->dwRecordType = RIL_SIMRECORDTYPE_DEDICATED;

        pBlob = (void *)prsrs;
        cbBlob = cbStruct;
        dwCode = RIL_RESULT_OK;
    }
Error:
    return hr;
}

//
//
//
HRESULT RILDrv_GetSimRecordStatus(DWORD dwParam, DWORD dwFileID)
{
    DWORD dwParameter1, dwParameter2, dwParameter3;
    DWORD dwRestrictedSIMCmd;
    LPBYTE lpbData = NULL;
    DWORD dwDataSize = 0;
    DWORD dwRetries = 0;
    DWORD dwRetryDelay = 0;
    DWORD dwCmdOption = CMDOPT_LOWESTPRIO;
    LPSTR szCmd = NULL;
    char szPath[MAX_PATH];
    char szPrefix[MAX_ATCMD_LEN];
    LPSTR szPrefixWalk = szPrefix;
    HRESULT hr = S_OK;
    DWORD dwRealFileID;
    PFN_CMD_ERROR pfnError = NULL;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }
    if (pHandle->GetDevice()->GetMultiModeSetting() == MULTIMODE_CONFIG_CDMA)
    {
        // In CDMA-only mode there isn't a SIM, so it makes no sense to send a AT+CRSM command.
        if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_GETSIMRECORDSTATUS, ParseForceFailureResponse, NULL, hr, NULL, (LPVOID)RIL_E_INVALIDINDEX))
        {
            hr = E_FAIL;
        }
        goto Error;
    }
    if (pHandle->GetDevice()->GetCurrentSIMType() == SIMTYPE_UNKNOWN)
    {
        // If SIM type is not known yet, try again for 10 seconds.
        dwRetries = 2;
        dwRetryDelay = 5000;
    }
    if (g_PowerManager.IsRadioOff() && dwFileID == SIM_EF_ECC)
    {
        // If radio is currently of and the SIM file is the ECC file, we have the bug in the
        // Windows Mobile MMI, that the SIM is queried before the module is turned on.
        // For this reason this command may need to wait for up to 1.5 minutes for the
        // radio to be powered on.
        if (!GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("RetriesForECCAccess"), &dwRetries))
            dwRetries = 16;
        dwRetryDelay = 5000;
        dwCmdOption = CMDOPT_LOWESTPRIO|CMDOPT_RETRYONRADIOOFF;
    }

    if (IsForbiddenSIMFile(dwFileID))
    {
        // Some SIM files must not be accessed with AT+CRSM. In this case the RIL return
        // invalid index as error instead of executing the AT command.
        if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_GETSIMRECORDSTATUS, ParseForceFailureResponse, NULL, hr, NULL, (LPVOID)RIL_E_INVALIDINDEX))
        {
            hr = E_FAIL;
        }
        goto Error;
    }

    dwRealFileID = GetUICCFileID(pHandle->GetDevice(), dwFileID);

    dwParameter1 = 0x00;
    dwParameter2 = 0x00;

    if (pHandle->GetDevice()->GetCurrentSIMType() != SIMTYPE_2G_SIM)    // Use 3G SIM as default, if SIM type is unknwon.
    {
        // For UICCs we read 63 bytes, that should always be more than enough.
        dwParameter3 = 63;
    }
    else
    {
        // Is this an elementary file?
        if (IsElementarySimFile(dwRealFileID))
        {
            // This is an elementary file -- send GET RESPONSE
            dwParameter3 = 15;  // min required length of response for EF
        }
        else
        {
            // This is not an elementary file
            dwParameter3 = 22;  // min required length of response for DF/MF
        }
    }

    dwRestrictedSIMCmd = g_rgdwRestrictedSIMCmds[RIL_SIMCMD_GETRESPONSE];

    // Add "AT+CRSM=<command>" to prefix string
    (void)_snprintfz(szPrefixWalk, MAX_ATCMD_LEN - (szPrefixWalk - szPrefix), "AT+CRSM=%u", dwRestrictedSIMCmd);
    szPrefixWalk = strchr(szPrefixWalk, '\0');  // NO_TYPO: 27

    // Add ",<fileid>" to prefix string
    (void)_snprintfz(szPrefixWalk, MAX_ATCMD_LEN - (szPrefixWalk - szPrefix), ",%u", dwRealFileID);
    szPrefixWalk = strchr(szPrefixWalk, '\0');  // NO_TYPO: 27

    // Add ",<p1>,<p2>,<p3>" to prefix string
    (void)_snprintfz(szPrefixWalk, MAX_ATCMD_LEN - (szPrefixWalk - szPrefix), ",%u,%u,%u",
                     dwParameter1, dwParameter2, dwParameter3);

    // If there's data, add a comma before that data
    if (dwDataSize)
    {
        (void)strncatz(szPrefix, ",\"", MAX_ATCMD_LEN);
    }

    // Add "<prefix>\"<data>\"<CR>"
    // NOTE: we take ownerswhip of allocated szCmd
    if (!ComposeCmdWithByteArray(szPrefix, lpbData, dwDataSize, dwDataSize?"\"\r":"\r", szCmd)) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }

    // Add <pathID>, if needed.
    if (NeedSIMPath(pHandle->GetDevice()->GetCurrentSIMType(), dwRealFileID, szPath, MAX_PATH))
    {
        szPrefixWalk = strchr(szCmd, '\r');
        if (dwDataSize == 0)
        {
            (void)_snprintfz(szPrefixWalk, MAX_ATCMD_LEN, ",");
            szPrefixWalk = strchr(szPrefixWalk, '\0');
        }
        (void)_snprintfz(szPrefixWalk, MAX_ATCMD_LEN - (szPrefixWalk - szCmd), ",\"%s\"\r", szPath);
    }

    // The GET_RESPONSE command may fail on MF or DF, even when they exist.
    // To solve this problem, the RIL generates an appropiate response, if the
    // radio returns an unspecific error messages.
    if (IsDForMFfile(dwFileID))
        pfnError = ParseGetSimRecordStatusError;

    if (pHandle->GetDevice()->GetEons()->MustReadIMSIbeforeReadingSIM())        // Read-IMSI-first workaround needed?
        (void)QueueInternalCmd(HCCMD_STANDARD, pHandle->GetDevice(), "AT+CRSM=192,28423,0,0,15\r", CMDOPT_LOWESTPRIO, APIID_GETSIMRECORDSTATUS, 0, 0, 0);

    if (!QueueCmdWithTimeout(HCCMD_STANDARD, pHandle, szCmd, NULL, dwCmdOption, APIID_GETSIMRECORDSTATUS, ParseGetSimRecordStatus, NULL, hr, 0, dwRetries, dwRetryDelay, pfnError, (LPVOID)(pHandle->GetDevice()), g_CMET_CRSM, PreInitSIMCmd)) {
        hr = E_FAIL;
        goto Error;
    }


Error:
    delete szCmd;
    return hr;
}

//
// Parses the response of AT+CRSM GET_RESPONSE command.
//
// *pParam will contain a DWORD with the file size depending of the type of
// the SIM file:
// Transparent: upper WORD: total file size; lower WORD: 1
// Cyclic/Linear: upper OWRD: size of one record; lower WORD: number of records
//
HRESULT ParseSimGetResponseInternal(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    HRESULT hr = S_OK;
    DWORD dwNumbers = 0;

    if (g_nSIMType == SIMTYPE_3G_SIM)
        hr = ParseNumbersOfRecordsFromSimRecordStatus3G(szRsp, pBlob, cbBlob, (LPVOID)&dwNumbers);
    else
        hr = ParseNumbersOfRecordsFromSimRecordStatus2G(szRsp, pBlob, cbBlob, (LPVOID)&dwNumbers);

    pBlob = NULL;
    cbBlob = 0;

    if (SUCCEEDED(hr) && dwNumbers > 0 && pParam != NULL) {
        DWORD *pdwParam = (DWORD *)pParam;

        *pdwParam = dwNumbers;
    }

    return hr;
}

//
// ParseNumbersOfRecordsFromSimRecordStatus3G()
// Parses the output of a GET_RESPONSE AT+CRSM SIM of a 3G SIM
//
// *pParam will contain a DWORD with the file size depending of the type of
// the SIM file:
// Transparent: upper WORD: total file size; lower WORD: 1
// Cyclic/Linear: upper OWRD: size of one record; lower WORD: number of records
//
HRESULT ParseNumbersOfRecordsFromSimRecordStatus3G(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    HRESULT hr = S_OK;
    RILSIMRESPONSE* prsr = NULL;
    UINT uiSize = 0;
    UINT uiPointer = 2;
    DWORD dwSize = 0;
    DWORD dwCount = 0;

    pBlob = NULL;
    cbBlob = 0;

    // First fill in the RILSIMRESPONSE structure
    void *pBlobTmp=NULL;
    UINT cbBlobTmp=0;

    hr = ParseSendRestrictedSimCmd(szRsp, pBlobTmp, cbBlobTmp, NULL);
    prsr = (RILSIMRESPONSE *)pBlobTmp;
    if (FAILED(hr)) {
        goto Error;
    }

    // Was there an error of some sort?
    hr = DetermineSimResponseError(prsr->dwStatusWord1, prsr->dwStatusWord2);
    if (FAILED(hr)) {
        goto Error;
    }

    uiSize = prsr->cbSize - sizeof(RILSIMRESPONSE);

    // The response has to be present and at least 4 bytes long
    if ((!(prsr->dwParams & RIL_PARAM_SR_RESPONSE)) || (uiSize < 4))
    {
        hr = E_FAIL;
        goto Error;
    }

    while (uiPointer+1 < uiSize)
    {
        // incomplete tag? end loop.
        if (uiPointer+prsr->pbResponse[uiPointer+1] > uiSize)
            break;

        if (prsr->pbResponse[uiPointer] == 0x82)
        {
            if ((prsr->pbResponse[uiPointer+2] & 0x7) == 2)
            {
                dwCount = (DWORD)prsr->pbResponse[uiPointer+6];
                dwSize = prsr->pbResponse[uiPointer+5] + (prsr->pbResponse[uiPointer+4] * 256);
            }
        }
        uiPointer += (2+prsr->pbResponse[uiPointer+1]);
    }

    if (pParam != NULL)
    {
        DWORD *pdw;

        pdw = (DWORD *)pParam;
        *pdw = (DWORD)(dwCount + (dwSize << 16));
    }

Error:
    (void)FreeBlob(prsr);
    return hr;
}

//
// ParseNumbersOfRecordsFromSimRecordStatus2G()
// Parses the output of a GET_RESPONSE AT+CRSM SIM of a 2G SIM
//
// *pParam will contain a DWORD with the file size depending of the type of
// the SIM file:
// Transparent: upper WORD: total file size; lower WORD: 1
// Cyclic/Linear: upper OWRD: size of one record; lower WORD: number of records
//
HRESULT ParseNumbersOfRecordsFromSimRecordStatus2G(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    HRESULT hr = S_OK;
    RILSIMRESPONSE* prsr = NULL;
    DWORD dwSize = 0;
    DWORD dwTotalSize = 0;
    DWORD dwRecordType = 0;

    pBlob = NULL;
    cbBlob = 0;

    // First fill in the RILSIMRESPONSE structure
    void *pBlobTmp=NULL;
    UINT cbBlobTmp=0;

    hr = ParseSendRestrictedSimCmd(szRsp, pBlobTmp, cbBlobTmp, NULL);
    prsr = (RILSIMRESPONSE *)pBlobTmp;
    if (FAILED(hr)) {
        goto Error;
    }

    // Was there an error of some sort?
    hr = DetermineSimResponseError(prsr->dwStatusWord1, prsr->dwStatusWord2);
    if (FAILED(hr)) {
        goto Error;
    }

    // The response has to be present and at least 7 bytes long
    if ((!(prsr->dwParams & RIL_PARAM_SR_RESPONSE)) || (prsr->cbSize - sizeof(RILSIMRESPONSE) < 7))
    {
        hr = E_FAIL;
        goto Error;
    }

    // Need at least 14 bytes for an elementary file
    if (prsr->cbSize - sizeof(RILSIMRESPONSE) < 14)
    {
        hr = E_FAIL;
        goto Error;
    }

    // Byte 14 tells us the file type
    switch (prsr->pbResponse[13])
    {
        case 0x00:
        case 0x02: // This is what OEM1 gives us, but it's not in 11.11!
            dwRecordType = RIL_SIMRECORDTYPE_TRANSPARENT;
            break;
        case 0x01:
            dwRecordType = RIL_SIMRECORDTYPE_LINEAR;
            break;
        case 0x03:
            dwRecordType = RIL_SIMRECORDTYPE_CYCLIC;
            break;
        default:
            break;
    }

    // Set more fields based on the file type
    dwTotalSize = (prsr->pbResponse[2] << 8) | prsr->pbResponse[3];
    if (dwRecordType == RIL_SIMRECORDTYPE_TRANSPARENT)
    {
        // Size is bytes 3 and 4
        dwSize = (dwTotalSize << 16) + 1;
    }
    else if (dwRecordType != RIL_SIMRECORDTYPE_UNKNOWN)
    {
        // Make sure we have a fifteenth byte in this case
        if (prsr->cbSize - sizeof(RILSIMRESPONSE) < 15)
        {
            hr = E_FAIL;
            goto Error;
        }

        dwSize = (prsr->pbResponse[14] << 16) + (dwTotalSize / prsr->pbResponse[14]);
    }

    if (pParam != NULL)
    {
        DWORD *pdw;

        pdw = (DWORD *)pParam;
        *pdw = (DWORD)(dwSize);
    }

Error:
    (void)FreeBlob(prsr);
    return hr;
}

//
// This thread read out the available ECC emergency numbers.
//
DWORD WINAPI ReadECCFilesThread(LPVOID pVoid)
{
    char szCmd[MAX_ATCMD_LEN];
    CRilHandle* pHandle = (CRilHandle *)pVoid;
    char szPath[MAX_PATH];
    BOOL fPath;

    if (IsForbiddenSIMFile(SIM_EF_ECC))
        return 0;

    g_fECCFieldAvailable = FALSE;
    g_uiECCCacheSize = 0;
    // Wait for SIM type to be known. Unfortunately the is no event mecahnism,
    // so polling is used.
    while (!g_PowerManager.IsRadioOff() && pHandle->GetCurrentSIMType() == SIMTYPE_UNKNOWN)
        Sleep(100);
    if (g_PowerManager.IsRadioOff())
        return 0;

    g_nSIMType = pHandle->GetCurrentSIMType();
    fPath = NeedSIMPath(pHandle->GetCurrentSIMType(), SIM_EF_ECC, szPath, MAX_PATH);
    if (g_nSIMType == SIMTYPE_3G_SIM)
    {
        DWORD dwNumRecords = 0;

        if (fPath)
            (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CRSM=192,28599,0,0,63,,\"%s\"\r", szPath);
        else
            (void)strncpyz(szCmd, "AT+CRSM=192,28599,0,0,63\r", MAX_ATCMD_LEN);

        if (QueueInternalCmdBlocking(HCCMD_STANDARD, pHandle, szCmd, CMDOPT_NONE, APIID_GETSIMRECORDSTATUS, 0, 2, 2000, ParseNumbersOfRecordsFromSimRecordStatus3G, NULL, (LPVOID)(&dwNumRecords)))
        {
            UINT iIdx;

            g_fECCFieldAvailable = TRUE;

            for (iIdx = 0; iIdx < (dwNumRecords&0xFFFF); iIdx++)
            {
                if (fPath)
                    (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CRSM=178,28599,%d,4,%d,,\"%s\"\r", iIdx+1, dwNumRecords>>16, szPath);
                else
                    (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CRSM=178,28599,%d,4,%d\r", iIdx+1, dwNumRecords>>16);
                (void)QueueInternalCmd(HCCMD_STANDARD, pHandle, szCmd, CMDOPT_NONE, APIID_SENDRESTRICTEDSIMCMD, 0, 0, 0, ParseSendRestrictedSimCmd, NULL, (LPVOID)(FILL_ECC_CACHE_CMD));
             }
        }
    }
    else if (g_nSIMType == SIMTYPE_2G_SIM)
    {
        DWORD dwLen = 0;

        if (fPath)
            (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CRSM=192,28599,0,0,15,,\"%s\"\r", szPath);
        else
            (void)strncpyz(szCmd, "AT+CRSM=192,28599,0,0,15\r", MAX_ATCMD_LEN);
        if (QueueInternalCmdBlocking(HCCMD_STANDARD, pHandle, szCmd, CMDOPT_NONE, APIID_GETSIMRECORDSTATUS, 0, 2, 2000, ParseNumbersOfRecordsFromSimRecordStatus2G, NULL, (LPVOID)(&dwLen)))
        {
            g_fECCFieldAvailable = TRUE;
            if (fPath)
                (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CRSM=176,28599,0,0,%d,,\"%s\"\r", dwLen, szPath);
            else
                (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CRSM=176,28599,0,0,%d\r", dwLen);
            (void)QueueInternalCmd(HCCMD_STANDARD, pHandle, szCmd, CMDOPT_NONE, APIID_SENDRESTRICTEDSIMCMD, 0, 0, 0, ParseSendRestrictedSimCmd, NULL, (LPVOID)(FILL_ECC_CACHE_CMD));
        }
    }
    return 0;
}

//
// RILDrv_GetSimToolkitProfile()
// All SAT function are handled by the CSimToolkitHandling class.
//
HRESULT RILDrv_GetSimToolkitProfile(DWORD dwParam)
{
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (pHandle) {
      return pHandle->GetDevice()->GetSimToolkit()->GetProfile(pHandle);
    }
    return E_FAIL;
}


//
// RILDrv_SetSimToolkitProfile()
// All SAT function are handled by the CSimToolkitHandling class.
//
HRESULT RILDrv_SetSimToolkitProfile(DWORD dwParam, const BYTE* /*lpbProfile*/, DWORD /*dwSize*/)
{
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (pHandle) {
      return pHandle->GetDevice()->GetSimToolkit()->SetProfile(pHandle);
    }
    return E_FAIL;
}


//
// RILDrv_TerminateSimToolkitSession()
// All SAT function are handled by the CSimToolkitHandling class.
//
HRESULT RILDrv_TerminateSimToolkitSession(DWORD dwParam, DWORD /*dwCause*/)
{
    HRESULT hr = E_NOTIMPL;
    RILSIMTOOLKITRSP Rsp;
    Rsp.dwParams = RIL_PARAM_SIMTKIT_RSP_TYPE | RIL_PARAM_SIMTKIT_RSP_RESPONSE;
    Rsp.dwResponse = 0;
    Rsp.dwType = SIM_NOTIFY_ENDSESSION;
    hr = RILDrv_SendSimToolkitCmdResponse(dwParam, &Rsp, 0, 0);
    return hr;
}

#ifdef RILDRV_CE6
//
// RILDrv_SendSimToolkitEnvelopeCmd()
// All SAT function are handled by the CSimToolkitHandling class.
//
HRESULT RILDrv_SendSimToolkitEnvelopeCmd(DWORD /*dwParam*/, const BYTE* /*lpbCommand*/, DWORD /*dwSize*/)
{
    return E_NOTIMPL;
}
#endif

//
// RILDrv_SendSimToolkitCmdResponse()
// All SAT function are handled by the CSimToolkitHandling class.
//
HRESULT RILDrv_SendSimToolkitCmdResponse(DWORD dwParam, const RILSIMTOOLKITRSP* pRsp, LPBYTE pDetails, const DWORD dwDetailSize)
{
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (pHandle) {
        if (INTEL_STYLE_SAT)
            return pHandle->GetDevice()->GetSimToolkit()->SendCmdResponseIntel(pHandle, pRsp, pDetails, dwDetailSize);
        else
            return pHandle->GetDevice()->GetSimToolkit()->SendCmdResponse(pHandle, pRsp, pDetails, dwDetailSize);
    }
    return E_FAIL;
}

//
// RILDrv_SendSimToolkitEventDownload()
// All SAT function are handled by the CSimToolkitHandling class.
//
HRESULT RILDrv_SendSimToolkitEventDownload(DWORD dwParam, const DWORD dwEvent, LPBYTE pData, const DWORD dwDataSize)
{
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (pHandle) {
      return pHandle->GetDevice()->GetSimToolkit()->SendEventDownload(pHandle, dwEvent, pData, dwDataSize);
    }
    return E_FAIL;
}

