/*****************************************************************************
 * Copyright (C) Gemalto M2M GmbH
 *****************************************************************************
 * Project: IMC-RIL-Driver
 *****************************************************************************
 * Author : Wireless Modules RD SW A2
 *****************************************************************************
 * $Workfile: Y:/z_ms-d/src/working/Quinn/RilDrv/supsvc.cpp $
 * $Revision: /main/ms-d/34 $
 *     $Date: 2013-03-01 09:10:19 $
 *   $Author: pmackent $
 *****************************************************************************
 * Target:
 *  Microsoft Windows Mobile 5.0
 *****************************************************************************
 * Description:
 *  Gemalto IMC RIL
 *****************************************************************************
 *
 * $Log: Y:/z_ms-d/src/working/Quinn/RilDrv/supsvc.cpp$ 
 * 
 * /main/ms-d/34   2013-03-01 09:10:19   pmackent 
 * No semicolon for *# on EHS5 (IPIS100085708)
 *
 * /main/ms-d/33   2013-02-26 15:08:01   pmackent
 * Adaptions for EHS5 1.290
 *
 * /main/ms-d/32   2012-11-26 16:28:22   pmackent
 * Accept CME error on Cancel USSD API
 *
 * /main/ms-d/31   2012-06-07 13:48:42   pmackent
 * Avoid *#-squences in CDMA mode
 *
 * /main/ms-d/30   2012-02-22 13:27:24   ctna0256
 * Encapsulate use of Intel AT command set
 *
 * /main/ms-d/29   2012-02-14 14:52:10   ctna0256
 * Improved comments
 *
 * /main/ms-d/28   2012-01-31 16:39:07   ctna0256
 * Added support for EHS5
 *
 * /main/ms-d/27   2011-10-20 09:18:38   ctna0256
 * Workaround for wrong CLIR URC
 *
 * /main/ms-d/26   2011-09-26 13:35:27   ctna0256
 * Comply with CE6 warning level 4
 *
 * /main/ms-d/25   2010-10-15 11:56:58   ctna0256
 * COLR now supported in PH8
 *
 * /main/ms-d/24   2010-07-14 10:40:50   ctna0256
 * Preparation: CCUG and ACM may not be supported in PH8
 *
 * /main/ms-d/23   2010-04-19 12:43:11   ctna0256
 * Corrected USSD length calculation for CE6
 *
 * /main/ms-d/22   2010-02-24 13:26:08   ctna0256
 * Made CE6 driver comply with radiometrics test
 *
 * /main/ms-d/21   2009-12-18 10:57:39   ctna0256
 * Translate **002 and **004 code (IPIS100061138)
 *
 * /main/ms-d/20   2008-08-22 10:28:11   ctna0256
 * Set Cinterion copyright text
 *
 * /main/ms-d/19   2008-08-21 09:35:40   ctna0256
 * Prepared for new disclaimer handling
 *
 * /main/ms-d/18   2008-02-26 14:14:08   mp49210
 * IPIS100045650: special handling of basic service 10 for GCF 27.22.6.2
 *
 * /main/ms-d/17   2008-01-11 12:17:05   mp49210
 * Remove unnecessary * in *# sequences
 *
 * /main/ms-d/16   2007-10-12 13:42:13   mp49210
 * Ignored Call Forwarding errors (optional, with registry key)
 *
 * /main/ms-d/15   2007-06-04 08:20:09   mp49210
 * IPIS100037515: send **05*...# and **052*...# sequences from the dialer with ATD instead of AT+CPIN command.
 *
 * /main/ms-d/14   2007-05-14 16:06:29   mp49210
 * IPIS100036184: wrong parsing of long USSD strings
 *
 * /main/ms-d/13   2007-04-23 14:07:53   mp49210
 * Fix for exception when canceling USSD while module is off
 *
 * /main/ms-d/12   2007-03-28 13:48:12   mp49210
 * Remove 'Classic RIL' specific code and comments
 *
 * /main/ms-d/11   2007-03-28 10:41:58   mp49210
 * Store CLIR value persistent
 *
 * /main/ms-d/10   2007-02-23 15:46:13   mp49210
 * Ignore error from AT+CUSD=2
 *
 * /main/ms-d/9   2007-02-12 16:26:59   mp49210
 * Workaround for *#43*xx#-Problem
 *
 * /main/ms-d/8   2007-02-09 15:15:26   mp49210
 * Lint compatibility
 *
 * /main/ms-d/7   2007-01-23 11:21:01   mp49210
 * Temporarily deactivate AT+CUSD=2
 *
 * /main/ms-d/6   2007-01-22 10:34:03   mp49210
 * Change in AT+CUSD character coding
 *
 * /main/ms-d/5   2006-12-11 16:20:40   mp49210
 * Re-Integration of *#-sequences for GCF approval
 *
 * /main/ms-d/4   2006-11-08 09:35:28   mp49210
 * No COLR in Quinn
 *
 * /main/ms-d/3   2006-11-06 11:26:26   mp49210
 * New AT+CCFC-Syntax in Quinn
 *
 * /main/ms-d/2   2006-11-02 14:59:10   mp49210
 * No *#-sequences in HC25
 *
 * /main/ms-d/1   2006-10-25 16:52:22   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"

//
// CallerID, DialedID, Closed User Group status values
//
static const DWORD g_rgdwSupSvcStats[] =
{
    RIL_SVCSTAT_DISABLED,           // 0
    RIL_SVCSTAT_ENABLED,            // 1
};
#define NUM_SUPSVCSTATS     (sizeof(g_rgdwSupSvcStats) / sizeof(DWORD))


//
// CallerID and DialedID provisioning values
//
static const DWORD g_rgdwSupSvcProv[] =
{
    RIL_SVCPROV_NOTPROVISIONED,     // 0
    RIL_SVCPROV_PROVISIONED,        // 1
    RIL_SVCPROV_UNKNOWN,            // 2
};
#define NUM_SUPSVCPROV      (sizeof(g_rgdwSupSvcProv) / sizeof(DWORD))


//
// HideID provisioning values
//
static const DWORD g_rgdwHideIDProv[] =
{
    RIL_SVCPROV_NOTPROVISIONED,     // 0
    RIL_SVCPROV_PROVISIONED,        // 1
    RIL_SVCPROV_UNKNOWN,            // 2
    RIL_SVCPROV_TEMPMODERESTRICTED, // 3
    RIL_SVCPROV_TEMPMODEALLOWED,    // 4
};
#define NUM_HIDEIDPROV   (sizeof(g_rgdwHideIDProv) / sizeof(DWORD))


//
// Closed User Group info values
//
static const DWORD g_rgdwClosedGroupInfos[] =
{
    RIL_CUGINFO_NONE,               // 0
    RIL_CUGINFO_SUPPRESSOA,         // 1
    RIL_CUGINFO_SUPRESSPREF,        // 2
    RIL_CUGINFO_SUPPRESSOAANDPREF,  // 3
};
#define NUM_CLOSEDGROUPINFOS    (sizeof(g_rgdwClosedGroupInfos) / sizeof(DWORD))


//
// Call Forwarding Reasons
//
static const DWORD g_rgdwFwdReasons[] =
{
    RIL_FWDREASON_UNCONDITIONAL,    // 0
    RIL_FWDREASON_MOBILEBUSY,       // 1
    RIL_FWDREASON_NOREPLY,          // 2
    RIL_FWDREASON_UNREACHABLE,      // 3
    RIL_FWDREASON_ALLFORWARDING,    // 4
    RIL_FWDREASON_ALLCONDITIONAL,   // 5
};
#define NUM_FWDREASONS          (sizeof(g_rgdwFwdReasons) / sizeof(DWORD))

static BOOL g_fAllowEnableHideCallerId = TRUE;
DWORD g_dwHideCallerId = RIL_SVCPROV_UNKNOWN;
DWORD g_dwCallerIdStatus = RIL_SVCPROV_PROVISIONED;
DWORD g_dwLastCUSSDCmd = 0;
extern BOOL g_fDisableCOLPInformation;
extern UINT g_nCOLPStatus;

#define MAX_USSD_STRING_LEN     180         // Max. length of USSD messages accepted by the radio.

// UseGSM0707Cmd() returns TRUE, if the RIL should use the AT+C commands rather
// than ATD*# sequences.
BOOL UseGSM0707Cmd(CRilInstanceHandle *pHandle)
{
    if (pHandle != NULL && AVOID_STARHASH_IN_CDMA) {
        // While in CDMA mode use only the AT+C commands. They might may, but
        // at leas they doesn't cause any trouble.
        if (pHandle->GetDevice()->GetCurrentTechnology() == TEC_MODE_CDMA)
            return TRUE;
    }
    return g_fUseGSM0707Cmds;
}

// Some module need the semicolon at ATD for *#-sequence, some do not.
const char *GetATDSemicolon()
{
    if (STARHASH_WITHOUT_SEMICOLON) return "";
    return ";";
}

// Translation InfoClass (Microsoft) -> BS-Code (GSM)
// Parameter:
// dwInfoClass - InfoClass (Microsoft)
// Return Value:
// 0 = translation not possible
// (other) - BS Code (GSM)
UINT RILDrv_FindBS4InfoClass(DWORD dwInfoClass)
{
    UINT uiBS = 0;
    UINT i;

    for (i = 0; i < NUM_BS_VALUES; i++)
    {
        if (g_rgInfoClassTable[i].dwInfoClass == dwInfoClass)
        {
            uiBS = g_rgInfoClassTable[i].uiBS;
        }
    }
    return uiBS;
}

//
//
//
static HRESULT ParseGetCallerIdSettings(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    UINT nValue;
    RILCALLERIDSETTINGS* prcids = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    prcids = (RILCALLERIDSETTINGS*)AllocBlob(sizeof(RILCALLERIDSETTINGS));
    if (!prcids) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prcids, 0x00, sizeof(RILCALLERIDSETTINGS));
    prcids->cbSize = sizeof(RILCALLERIDSETTINGS);

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

    if (NUM_SUPSVCSTATS > nValue) {
        prcids->dwStatus = g_rgdwSupSvcStats[nValue];
    } else {
        prcids->dwStatus = RIL_SVCSTAT_UNKNOWN;
    }

    prcids->dwParams |= RIL_PARAM_CIDS_STATUS;

    // Parse ",<provisioning><postfix>"
    if (!MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp)   ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    if (NUM_SUPSVCPROV > nValue) {
        prcids->dwProvisioning = g_rgdwSupSvcProv[nValue];
    } else {
        prcids->dwProvisioning = RIL_SVCPROV_UNKNOWN;
    }
    prcids->dwParams |= RIL_PARAM_CIDS_PROVISIONING;

    // Workaround: the Microsoft MMI displays dwStatus when it should display dwProvisioning.
    if (prcids->dwProvisioning == RIL_SVCPROV_UNKNOWN)
        prcids->dwStatus = RIL_SVCSTAT_UNKNOWN;

    pBlob = (void*)prcids;
    cbBlob = sizeof(RILCALLERIDSETTINGS);

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

//
//
//
HRESULT RILDrv_GetCallerIdSettings(DWORD dwParam)
{
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    DWORD dwRetries = 0;
    DWORD dwRetryDelay = 0;
    PFN_PRE_CMD_CHECK pPreFunc = NULL;

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

    if (!QueueCmdWithTimeout(HCCMD_STANDARD, pHandle, "AT+CLIP?\r", NULL, CMDOPT_NONE, APIID_GETDIALEDIDSETTINGS, ParseGetCallerIdSettings, NULL, hr, 0, dwRetries, dwRetryDelay, NULL, NULL, NULL, pPreFunc)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}


//
//
//
HRESULT RILDrv_SetCallerIdStatus(DWORD dwParam, DWORD dwStatus)
{
    UINT i;
    UINT nValue = 0;
    char szCmd[MAX_ATCMD_LEN];
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = (CRilInstanceHandle*)dwParam;
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    for (i = 0; i < NUM_SUPSVCSTATS; i++) {
        if (dwStatus == g_rgdwSupSvcStats[i]) {
            nValue = i;
            break;
        }
    }
    if (NUM_SUPSVCSTATS == i) {
        hr = E_INVALIDARG;
        goto Error;
    }

    (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CLIP=%d\r", nValue);
    g_dwCallerIdStatus = dwStatus;
    if (!QueueCmd(HCCMD_STANDARD, pHandle, szCmd, CMDOPT_NONE, APIID_SETCALLERIDSTATUS, NULL, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}


//
//
//
static HRESULT ParseGetHideIdSettings(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    UINT nValue;
    RILHIDEIDSETTINGS* prhids = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    prhids = (RILHIDEIDSETTINGS*)AllocBlob(sizeof(RILHIDEIDSETTINGS));
    if (!prhids) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prhids, 0x00, sizeof(RILHIDEIDSETTINGS));
    prhids->cbSize = sizeof(RILHIDEIDSETTINGS);

    // Parse "<prefix>+CLIR: <status>"
    if (!ParseRspPrefix(szRsp, szRsp)                  ||
        !MatchStringBeginning(szRsp, "+CLIR: ", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    if (NUM_HIDEIDSTATS > nValue) {
        prhids->dwStatus = g_rgdwHideIDStats[nValue];
    } else {
        prhids->dwStatus = RIL_SVCSTAT_UNKNOWN;
    }
    if (g_dwHideCallerId != RIL_SVCPROV_UNKNOWN && (prhids->dwStatus == RIL_SVCSTAT_UNKNOWN || prhids->dwStatus == RIL_SVCSTAT_DEFAULT))
    {
        prhids->dwStatus = g_dwHideCallerId;
    }
    if (prhids->dwStatus == RIL_SVCSTAT_UNKNOWN)
        prhids->dwStatus = RIL_SVCSTAT_DEFAULT;
    prhids->dwParams |= RIL_PARAM_HIDS_STATUS;

    // Parse ",<provisioning><postfix>"
    if (!MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp)   ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    if (NUM_HIDEIDPROV > nValue) {
        prhids->dwProvisioning = g_rgdwHideIDProv[nValue];
    } else {
        prhids->dwProvisioning = RIL_SVCPROV_UNKNOWN;
    }
    prhids->dwParams |= RIL_PARAM_HIDS_PROVISIONING;

    if (RIL_SVCPROV_NOTPROVISIONED == prhids->dwProvisioning) {
        g_fAllowEnableHideCallerId = FALSE;
    } else {
        g_fAllowEnableHideCallerId = TRUE;
    }

    pBlob = (void*)prhids;
    cbBlob = sizeof(RILHIDEIDSETTINGS);

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


//
//
//
HRESULT RILDrv_GetHideIdSettings(DWORD dwParam)
{
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    if (!QueueCmd(HCCMD_STANDARD, pHandle, ("AT+CLIR?\r"), CMDOPT_NONE, APIID_GETHIDEIDSETTINGS, ParseGetHideIdSettings, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

    if (WRONG_CLIR_URC) {
        // Bug: the expected response of the AT+CLIR? command might be followed
        // by an unexpectded second response. In order to avoid, that this response
        // mistakenly interpreted the dummy AT is sent.
        (void)QueueInternalCmd(HCCMD_STANDARD, pHandle->GetDevice(), "AT\r", CMDOPT_NONE, APIID_GETHIDEIDSETTINGS, 0, 0, 0, NULL, NULL);
    }

Error:
    return hr;
}


static HRESULT ParseSetHideIdSettings(LPCSTR /*szRsp*/, void*& /*pBlob*/, UINT& /*cbBlob*/, LPVOID pParam)
{
    (void)SetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, REG_LAST_CLIR, (DWORD)pParam);
    return S_OK;
}

//
//
//
HRESULT RILDrv_SetHideIdStatus(DWORD dwParam, DWORD dwStatus)
{
    UINT i;
    UINT nValue = 0;
    char szCmd[MAX_ATCMD_LEN];
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    //do not allow CLIR invocation if it is not provisioned
    if ((RIL_SVCSTAT_ENABLED == dwStatus) && !g_fAllowEnableHideCallerId) {
        hr = RIL_E_SVCNOTSUBSCRIBED;
        goto Error;
    }

    for (i = 0; i < NUM_HIDEIDSTATS; i++) {
        if (dwStatus == g_rgdwHideIDStats[i]) {
            nValue = i;
            break;
        }
    }
    if (NUM_HIDEIDSTATS == i) {
        hr = E_INVALIDARG;
        goto Error;
    }

    g_dwHideCallerId = dwStatus;
    (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CLIR=%u\r", nValue);
    if (!QueueCmd(HCCMD_STANDARD, pHandle, szCmd, CMDOPT_NONE, APIID_SETHIDEIDSTATUS, ParseSetHideIdSettings, NULL, hr, NULL, (LPVOID)dwStatus)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}


//
//
//
static HRESULT ParseGetDialedIdSettings(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    UINT nValue;
    RILDIALEDIDSETTINGS* prdids = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    prdids = (RILDIALEDIDSETTINGS*)AllocBlob(sizeof(RILDIALEDIDSETTINGS));
    if (!prdids) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prdids, 0x00, sizeof(RILDIALEDIDSETTINGS));
    prdids->cbSize = sizeof(RILDIALEDIDSETTINGS);

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

    if (g_fDisableCOLPInformation)      // COLP is supressed, so we have to use the stored value.
        nValue = g_nCOLPStatus;

    if (NUM_SUPSVCSTATS > nValue) {
        prdids->dwStatus = g_rgdwSupSvcStats[nValue];
    } else {
        prdids->dwStatus = RIL_SVCSTAT_UNKNOWN;
    }
    prdids->dwParams |= RIL_PARAM_DIDS_STATUS;

    // Parse ",<provisioning><postfix>"
    if (!MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp)   ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    if (NUM_SUPSVCPROV > nValue) {
        prdids->dwProvisioning = g_rgdwSupSvcProv[nValue];
    } else {
        prdids->dwProvisioning = RIL_SVCPROV_UNKNOWN;
    }
    prdids->dwParams |= RIL_PARAM_DIDS_PROVISIONING;

    pBlob = (void*)prdids;
    cbBlob = sizeof(RILDIALEDIDSETTINGS);

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


//
//
//
HRESULT RILDrv_GetDialedIdSettings(DWORD dwParam)
{
    HRESULT hr = S_OK;
    char szCmd[MAX_ATCMD_LEN];
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    DWORD dwRetries = 0;
    DWORD dwRetryDelay = 0;
    PFN_PRE_CMD_CHECK pPreFunc = NULL;

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

    (void)strncpyz(szCmd, "AT+COLP?\r", MAX_ATCMD_LEN);

    if (!QueueCmdWithTimeout(HCCMD_STANDARD, pHandle, szCmd, NULL, CMDOPT_NONE, APIID_GETDIALEDIDSETTINGS, ParseGetDialedIdSettings, NULL, hr, 0, dwRetries, dwRetryDelay, NULL, NULL, NULL, pPreFunc)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}

//
//
//
HRESULT RILDrv_SetDialedIdStatus(DWORD dwParam, DWORD dwStatus)
{
    UINT i;
    UINT nValue = 0;
    char szCmd[MAX_ATCMD_LEN];
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = (CRilInstanceHandle*)dwParam;
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    for (i = 0; i < NUM_SUPSVCSTATS; i++) {
        if (dwStatus == g_rgdwSupSvcStats[i]) {
            nValue = i;
            break;
        }
    }
    if (NUM_SUPSVCSTATS == i) {
        hr = E_INVALIDARG;
        goto Error;
    }

    // If COLP is disabled via registry key we must block the activation here.
    g_nCOLPStatus = nValue;
    if (g_fDisableCOLPInformation && dwStatus == RIL_SVCSTAT_ENABLED)
        nValue = 0;

    (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+COLP=%d\r", nValue);
    if (!QueueCmd(HCCMD_STANDARD, pHandle, szCmd, CMDOPT_NONE, APIID_SETDIALEDIDSTATUS, NULL, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}


//
//
//
static HRESULT ParseGetHideConnectedIdSettings(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    UINT nValue;
    RILHIDECONNECTEDIDSETTINGS* prdids = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    prdids = (RILHIDECONNECTEDIDSETTINGS*)AllocBlob(sizeof(RILHIDECONNECTEDIDSETTINGS));
    if (!prdids) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prdids, 0x00, sizeof(RILDIALEDIDSETTINGS));
    prdids->cbSize = sizeof(RILDIALEDIDSETTINGS);

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

#ifdef RIL_PARAM_DIDS_STATUS
    if (NUM_SUPSVCSTATS > nValue) {
        prdids->dwStatus = g_rgdwSupSvcStats[nValue];
    } else {
        prdids->dwStatus = RIL_SVCSTAT_UNKNOWN;
    }
    prdids->dwParams |= RIL_PARAM_DIDS_STATUS;
#endif

    if (MatchStringBeginning(szRsp, ",", szRsp)) {
        // Parse ",<provisioning><postfix>"  (optional)
        if (!ParseUInt(szRsp, TRUE, nValue, szRsp)   ||
            !ParseRspPostfix(szRsp, szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        if (NUM_SUPSVCPROV > nValue) {
            prdids->dwProvisioning = g_rgdwSupSvcProv[nValue];
        } else {
            prdids->dwProvisioning = RIL_SVCPROV_UNKNOWN;
        }
        prdids->dwParams |= RIL_PARAM_DIDS_PROVISIONING;
    }

    pBlob = (void*)prdids;
    cbBlob = sizeof(RILHIDECONNECTEDIDSETTINGS);

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


//
//
//
HRESULT RILDrv_GetHideConnectedIdSettings(DWORD dwParam)
{
    char szCmd[MAX_ATCMD_LEN];
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    if (STARHASH_WITHOUT_SEMICOLON)
        (void)strncpyz(szCmd, "ATD*#77#\r", MAX_ATCMD_LEN);
    else
        (void)strncpyz(szCmd, "ATD*#77#;\r", MAX_ATCMD_LEN);
    (void)TranslateATDCmds(szCmd, MAX_ATCMD_LEN);
    if (!QueueCmd(HCCMD_STANDARD, pHandle, szCmd, CMDOPT_NONE, APIID_GETHIDECONNECTEDIDSETTINGS, ParseGetHideConnectedIdSettings, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}

//
//
//
HRESULT RILDrv_SetHideConnectedIdStatus(DWORD dwParam, DWORD dwStatus)
{
    UINT i;
    UINT nValue = 0;
    char szCmd[MAX_ATCMD_LEN];
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = (CRilInstanceHandle*)dwParam;
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }
    if (!SUPPORTS_FULL_COLR) {
        hr = E_NOTIMPL;
        goto Error;
    }

    for (i = 0; i < NUM_SUPSVCSTATS; i++) {
        if (dwStatus == g_rgdwSupSvcStats[i]) {
            nValue = i;
            break;
        }
    }
    if (NUM_SUPSVCSTATS == i) {
        hr = E_INVALIDARG;
        goto Error;
    }

    (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "ATD%s77#%s\r", nValue==0 ? "#" : "*", GetATDSemicolon());
    if (!QueueCmd(HCCMD_STANDARD, pHandle, szCmd, CMDOPT_NONE, APIID_SETDIALEDIDSTATUS, NULL, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}

//
//
//
static HRESULT ParseGetClosedGroupSettings(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    UINT nValue;
    RILCLOSEDGROUPSETTINGS* prcgs = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    prcgs = (RILCLOSEDGROUPSETTINGS*)AllocBlob(sizeof(RILCLOSEDGROUPSETTINGS));
    if (!prcgs) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prcgs, 0x00, sizeof(RILCLOSEDGROUPSETTINGS));
    prcgs->cbSize = sizeof(RILCLOSEDGROUPSETTINGS);

    // Parse "<prefix>+CCUG: <status>"
    if (!ParseRspPrefix(szRsp, szRsp)                  ||
        !MatchStringBeginning(szRsp, "+CCUG: ", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    if (NUM_SUPSVCSTATS > nValue) {
        prcgs->dwStatus = g_rgdwSupSvcStats[nValue];
    } else {
        prcgs->dwStatus = RIL_SVCSTAT_UNKNOWN;
    }
    prcgs->dwParams |= RIL_PARAM_CGS_STATUS;

    // Parse ",<index>"
    if (!MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseUIntAndVerifyAbove(szRsp, TRUE, 11, nValue, szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }
    if (10 == nValue) {
        // Special index value that means "no index"
        prcgs->dwIndex = RIL_CUGINDEX_NONE;
    } else {
        prcgs->dwIndex = nValue;
    }
    prcgs->dwParams |= RIL_PARAM_CGS_INDEX;

    // Parse ",<info><postfix>"
    if (!MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp)   ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    if (NUM_CLOSEDGROUPINFOS > nValue) {
        prcgs->dwInfo = g_rgdwClosedGroupInfos[nValue];
    } else {
        prcgs->dwInfo = RIL_CUGINFO_NONE;
    }
    prcgs->dwParams |= RIL_PARAM_CGS_INFO;

    pBlob = (void*)prcgs;
    cbBlob = sizeof(RILCLOSEDGROUPSETTINGS);

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


//
//
//
HRESULT RILDrv_GetClosedGroupSettings(DWORD dwParam)
{
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }
    if (!SUPPORTS_CCUG) {
        hr = E_NOTIMPL;
        goto Error;
    }

    if (!QueueCmd(HCCMD_STANDARD, pHandle, "AT+CCUG?\r", CMDOPT_NONE, APIID_GETCLOSEDGROUPSETTINGS, ParseGetClosedGroupSettings,
                  NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:

    return hr;
}


//
//
//
HRESULT RILDrv_SetClosedGroupSettings(DWORD dwParam, const RILCLOSEDGROUPSETTINGS* lpSettings)
{
    char szCmd[MAX_ATCMD_LEN];
    LPSTR szWalk = szCmd;
    UINT i;
    UINT nValue = 0;
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }
    if (!SUPPORTS_CCUG) {
        hr = E_NOTIMPL;
        goto Error;
    }

    if (!(lpSettings->dwParams & RIL_PARAM_CGS_STATUS)) {
        hr = E_INVALIDARG;
        goto Error;
    }

    for (i = 0; i < NUM_SUPSVCSTATS; i++) {
        if (lpSettings->dwStatus == g_rgdwSupSvcStats[i]) {
            nValue = i;
            break;
        }
    }
    if (NUM_SUPSVCSTATS == i) {
        hr = E_INVALIDARG;
        goto Error;
    }

    (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "AT+CCUG=%u", nValue);
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27

    if (lpSettings->dwParams & RIL_PARAM_CGS_INDEX) {
        nValue = (RIL_CUGINDEX_NONE == lpSettings->dwIndex ? 10 : lpSettings->dwIndex);
        (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), ",%u", nValue);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    }

    if (lpSettings->dwParams & RIL_PARAM_CGS_INFO) {
        for (i = 0; i < NUM_CLOSEDGROUPINFOS; i++) {
            if (lpSettings->dwInfo == g_rgdwClosedGroupInfos[i]) {
                nValue = i;
                break;
            }
        }
        if (NUM_CLOSEDGROUPINFOS == i) {
            hr = E_INVALIDARG;
            goto Error;
        }

        (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), ",%u", nValue);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    }
    (void)strncpyz(szWalk, "\r", MAX_ATCMD_LEN - (szWalk - szCmd));  // NO_TYPO: 30

    if (!QueueCmd(HCCMD_STANDARD, pHandle, szCmd, CMDOPT_NONE, APIID_SETCLOSEDGROUPSETTINGS, NULL, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:

    return hr;
}


//
//
//
static HRESULT ParseGetCallForwardingSettings(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    UINT i;
    UINT nValue = 0;
    char szAddress[MAXLENGTH_ADDRESS];
#ifdef ETSI_SPEC
    char szSubAddress[MAXLENGTH_SUBADDR];
#endif
    UINT nUsed = 0;
    UINT nAllocated = 0;
    RILCALLFORWARDINGSETTINGS* rgrcfs = NULL;
#ifdef ETSI_SPEC
    BOOL fSubAddressPresent = FALSE;
#endif
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    // Parse "<prefix>"
    if (!ParseRspPrefix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    // Parse "+CCFC: "
    while (MatchStringBeginning(szRsp, "+CCFC: ", szRsp)) {
        if (nUsed == nAllocated) {
            if (!AllocateOrReallocateStorage((BYTE**)&rgrcfs, sizeof(RILCALLFORWARDINGSETTINGS), nUsed, &nAllocated, SUPSVC_ALLOC_NUMBER)) {
                hr = E_OUTOFMEMORY;
                goto Error;
            }
        }

        (void)memset(&rgrcfs[nUsed], 0x00, sizeof(RILCALLFORWARDINGSETTINGS));
        rgrcfs[nUsed].cbSize = sizeof(RILCALLFORWARDINGSETTINGS);

        // Parse "<status>"
        if (!ParseUInt(szRsp, TRUE, nValue, szRsp)) {
            goto Continue;
        }
        if (NUM_SUPSVCSTATS > nValue) {
            rgrcfs[nUsed].dwStatus = g_rgdwSupSvcStats[nValue];
        } else {
            rgrcfs[nUsed].dwStatus = RIL_SVCSTAT_UNKNOWN;
        }
        rgrcfs[nUsed].dwParams |= RIL_PARAM_CFS_STATUS;

        // Parse ",<class>"
        if (!MatchStringBeginning(szRsp, ",", szRsp) ||
            !ParseUInt(szRsp, TRUE, nValue, szRsp)) {
            goto Continue;
        }
        for (i = 0 ; i < NUM_INFOCLASSES; i++) {
            if (nValue & (0x01 << i)) {
                rgrcfs[nUsed].dwInfoClasses |= g_rgdwInfoClasses[i];
            }
        }
        rgrcfs[nUsed].dwParams |= RIL_PARAM_CFS_INFOCLASSES;

        // Parse ","
        if (MatchStringBeginning(szRsp, ",", szRsp)) {
            // Parse "<address>,<type>"
            if (!ParseString(szRsp, szAddress, MAXLENGTH_ADDRESS, szRsp)     ||
                !MatchStringBeginning(szRsp, ",", szRsp)                     ||
                !ParseUIntAndVerifyAbove(szRsp, FALSE, 0x100, nValue, szRsp) ||
                !StringToRILAddress(szAddress, (BYTE)nValue, rgrcfs[nUsed].raAddress)) {
                goto Continue;
            }
            rgrcfs[nUsed].dwParams |= RIL_PARAM_CFS_ADDRESS;
            // Parse ","
            if (MatchStringBeginning(szRsp, ",", szRsp)) {
                // Parse "<subaddr>,<subaddr_type>"
#ifdef ETSI_SPEC
                if (ParseString(szRsp, szSubAddress, MAXLENGTH_SUBADDR, szRsp)) {
                    fSubAddressPresent = TRUE;
                }

                // Parse ","
                if (!MatchStringBeginning(szRsp, ",", szRsp)) {
                    goto Continue;
                }

                // Parse "<subaddr_type>"
                if (ParseUIntAndVerifyAbove(szRsp, FALSE, 0x100, nValue, szRsp) &&
                    fSubAddressPresent                                          &&
                    StringToRILSubAddress(szSubAddress, (BYTE)nValue, rgrcfs[nUsed].rsaSubAddress)) {
                    rgrcfs[nUsed].dwParams |= RIL_PARAM_RPI_SUBADDRESS;
                }

                // Parse ","
                if (MatchStringBeginning(szRsp, ",", szRsp)) {
                    // Parse "<time>
                    if (ParseUInt(szRsp, FALSE, nValue, szRsp)) {
                        rgrcfs[nUsed].dwDelayTime = nValue;
                        rgrcfs[nUsed].dwParams |= RIL_PARAM_CFS_DELAYTIME;
                    }
                }
#else
                if (ParseUInt(szRsp, FALSE, nValue, szRsp)) {
                    rgrcfs[nUsed].dwDelayTime = nValue;
                    rgrcfs[nUsed].dwParams |= RIL_PARAM_CFS_DELAYTIME;
                }
#endif
            }
        }

        // Increment the array index
        nUsed++;

Continue:
        // Find "<postfix>"
        if (!FindRspPostfix(szRsp, szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        // Skip over additional empty line in EHS5.
        if (UNNECESSARY_BLANK_LINES_IN_RESPONSE)
            (void)FindRspPostfix(szRsp, szRsp);
    }

    pBlob = (void*)rgrcfs;
    cbBlob = nUsed * sizeof(RILCALLFORWARDINGSETTINGS);

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

//
// Clean ATD string from unnecessary tailing *(s) in front of #.
//
void CleanATDString(LPSTR lpszATDString)
{
    DWORD dwReg = 0;
    INT nLen;

    // To have compatibility with the older RIL versions, the cleaning
    // of the ATD string can be disabled.
    GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("PreventStarHashCleanUp"), &dwReg);
    if (dwReg == 1)
        return;

    nLen = strlen(lpszATDString);
    while (nLen > 4)
    {
        if (lpszATDString[nLen - 1] != '\r' || lpszATDString[nLen - 2] != ';' || lpszATDString[nLen - 3] != '#' || lpszATDString[nLen - 4] != '*')
            break;
        lpszATDString[nLen - 1] = 0;
        lpszATDString[nLen - 2] = '\r';
        lpszATDString[nLen - 3] = ';';
        lpszATDString[nLen - 4] = '#';
        nLen--;
    }
    return;
}


// Generation of an ATD-*#-Code instead of AT+CCFC (if possible)
// Parameters: see RILDrv_GetCallForwardingSettings()
// Return Value:
// TRUE - ATD-string created
// FALSE - No corroponding ATD available
static BOOL RILDrv_GenerateATDString4CCFC(DWORD dwReason, DWORD dwInfoClass, LPSTR lpszATDString, DWORD dwStatus, LPCSTR lpcstrDN, DWORD dwDelayTime)
{
    BOOL bATDavailable = FALSE;
    UINT uiBS;

    uiBS = RILDrv_FindBS4InfoClass(dwInfoClass);
    if (uiBS > 0)
    {
        UINT i;

        for (i = 0; i < NUM_FORWARDING_VALUES; i++)
        {
            if (g_rgForwardingTypeTable[i].dwForwardingReason == dwReason && g_rgForwardingTypeTable[i].dwForwardingStatus == dwStatus)
            {
                char szBS[6];

                if (uiBS >= 100)
                    szBS[0] = '\0';
                else
                    (void)_snprintfz(szBS, 6, "%d", uiBS);

                if (!g_rgForwardingTypeTable[i].bNeedsT)
                {
                    (void)_snprintfz(lpszATDString, SECUR_CMDBUF_LENGTH, "ATD%s*%s*%s#%s\r", g_rgForwardingTypeTable[i].szATD, lpcstrDN, szBS, GetATDSemicolon());
                }
                else
                {
                    if (dwDelayTime == 0)
                        (void)_snprintfz(lpszATDString, SECUR_CMDBUF_LENGTH, "ATD%s*%s*%s*#%s\r", g_rgForwardingTypeTable[i].szATD, lpcstrDN, szBS, GetATDSemicolon());
                    else
                        (void)_snprintfz(lpszATDString, SECUR_CMDBUF_LENGTH, "ATD%s*%s*%s*%d#%s\r", g_rgForwardingTypeTable[i].szATD, lpcstrDN, szBS, dwDelayTime, GetATDSemicolon());
                }
                bATDavailable = TRUE;
                break;
            }
        }
    }

    if (bATDavailable)
        CleanATDString(lpszATDString);

    return bATDavailable;
}

static BOOL GetDelayTimeFromSequenceString(DWORD dwReason, BOOL &bDelayTime, DWORD &dwDelayTime)
{
    BOOL bFoundInString = FALSE;

    if (strlen(g_szSequenceFromDialer) > 4)
    {
        if ((dwReason == RIL_FWDREASON_ALLFORWARDING && strncmp(g_szSequenceFromDialer, "**002*", 6) == 0) ||
             (dwReason == RIL_FWDREASON_ALLCONDITIONAL && strncmp(g_szSequenceFromDialer, "**004*", 6) == 0) ||
             (dwReason == RIL_FWDREASON_NOREPLY && strncmp(g_szSequenceFromDialer, "**61*", 5) == 0))
        {
            int iToken;
            char *szToken;

            iToken = 0;
            szToken = strtok(g_szSequenceFromDialer+2, "*");
            while (szToken != NULL)
            {
                if (iToken == 2)
                {
                    bDelayTime = FALSE;
                    dwDelayTime = 0;
                    bFoundInString = TRUE;
                }
                if (iToken == 3 && atoi(szToken) > 0)
                {
                    bDelayTime = TRUE;
                    dwDelayTime = atoi(szToken);
                }
                szToken = strtok(NULL, "*");
                iToken ++;
            }
        }
    }

    g_szSequenceFromDialer[0] = 0;

    return bFoundInString;
}

static BOOL GetBasicServiceFromSequenceString(DWORD dwReason, BOOL &bBasicService, DWORD &dwBasicService)
{
    BOOL bFoundInString = FALSE;

    if (strlen(g_szSequenceFromDialer) > 4)
    {
        if ((dwReason == RIL_FWDREASON_UNCONDITIONAL && (strncmp(g_szSequenceFromDialer, "*21*", 4) == 0 ||
                                                                                                strncmp(g_szSequenceFromDialer, "#21*", 4) == 0 ||
                                                                                                strncmp(g_szSequenceFromDialer, "**21*", 5) == 0 ||
                                                                                                strncmp(g_szSequenceFromDialer, "##21*", 5) == 0 ||
                                                                                                strncmp(g_szSequenceFromDialer, "*#21*", 5) == 0)))
        {
            int iToken;
            char *szToken;

            iToken = 0;
            szToken = strstr(g_szSequenceFromDialer+2, "*");
            while (szToken != NULL)
            {
                if (iToken == 0)
                {
                    bBasicService = FALSE;
                    dwBasicService = 0;
                    bFoundInString = TRUE;
                }
                if (iToken == 1 && atoi(szToken+1) > 0)
                {
                    bBasicService = TRUE;
                    dwBasicService = atoi(szToken+1);
                }
                szToken = strstr(szToken+1, "*");
                iToken ++;
            }
        }
    }

    return bFoundInString;
}

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

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

    // The 'ERROR' should be ignored in this case (registry key set).
    if (MatchStringAnywhere(szRsp, "ERROR", szDummy))
    {
        // Since this isn't considered an error, we reconfigure this responnse to be an 'OK' instead.
        if (pBlob != NULL)
            (void)FreeBlob(pBlob);
        pBlob = NULL;
        cbBlob = 0;
        dwCode = RIL_RESULT_OK;
    }
    return S_OK;
}

PFN_CMD_ERROR ErrorHandlerForCallForwarding()
{
    PFN_CMD_ERROR pErrorHandler = NULL;
    DWORD dwRegSetting = 0;

    // If this registry key is set, all call forwarding error will be treated as "OK".
    (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("CFignoreErr"), &dwRegSetting);
    if (dwRegSetting == 1)
        pErrorHandler = ParseIgnoreCFError;

    return pErrorHandler;
}

//
//
//
HRESULT RILDrv_GetCallForwardingSettings(DWORD dwParam, DWORD dwReason, DWORD dwInfoClasses)
{
    DWORD dwRetries = 0;
    DWORD dwRetryDelay = 0;
    PFN_PRE_CMD_CHECK pPreFunc = NULL;
    UINT i;
    UINT nValue = 0;
    DWORD dwClass = 0;
    char szCmd[MAX_ATCMD_LEN];
    LPSTR szWalk = szCmd;
    HRESULT hr = S_OK;
    BOOL bBasicService = FALSE;
    DWORD dwBasicService = 0;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    if (dwInfoClasses == RIL_INFOCLASS_NONE)
        dwInfoClasses = RIL_INFOCLASS_DEFAULT_CALLFORWARING;

    if (GetBasicServiceFromSequenceString(dwReason, bBasicService, dwBasicService)) {
        // Special treatment of Basic Service 10 for unconditional CF: for GCF 27.22.6.2
        // this sequence must be sent do the radio, although this is an illegal BS value.
        if (bBasicService == TRUE && dwBasicService == 10) {
            (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "ATD%s%s\r", g_szSequenceFromDialer, GetATDSemicolon());
            (void)TranslateATDCmds(szCmd, MAX_ATCMD_LEN);
            if (!QueueCmdWithTimeout(HCCMD_STANDARD, pHandle, szCmd, NULL, CMDOPT_NONE, APIID_SETCALLFORWARDINGSTATUS, NULL, NULL, hr, 0, dwRetries, dwRetryDelay, ErrorHandlerForCallForwarding(), NULL, NULL, pPreFunc)) {
                hr = E_FAIL;
            }
            g_szSequenceFromDialer[0] = 0;
            goto Error;
        }
    }

    // In order to pass PTCRB/GCF Testcases: 31.2.1.6.1 & 31.2.1.6.2, the RIL first tries to generate an ATD*#-String,
    // only if this fails, it uses AT+CCFC. If use of *#-sequneces is disabled, the AT+CCFC is always used.
    if (UseGSM0707Cmd(pHandle) || !RILDrv_GenerateATDString4CCFC(dwReason, dwInfoClasses, szCmd, RIL_FORWARDINGSTATUS_QUERY, "", 0))
    {
        for (i = 0; i < NUM_FWDREASONS; i++) {
            if (dwReason == g_rgdwFwdReasons[i]) {
                nValue = i;
                break;
            }
        }
        if (NUM_FWDREASONS == i) {
            hr = E_INVALIDARG;
            goto Error;
        }

        (void)strncpyz(szCmd, "AT", MAX_ATCMD_LEN);
        szWalk = strchr(szCmd, '\0');  // NO_TYPO: 27

        if (dwInfoClasses == (RIL_INFOCLASS_VOICE | RIL_INFOCLASS_DATA | RIL_INFOCLASS_FAX)) {
            (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "+CCFC=%u,2\r", nValue);
        }
        else {
            for (i = 0 ; i < NUM_INFOCLASSES; i++) {
                if (dwInfoClasses & g_rgdwInfoClasses[i]) {
                    dwClass |= (0x01 << i);
                }
            }

            (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "+CCFC=%u,2,,,%u\r", nValue, dwClass);
        }

        if (!QueueCmdWithTimeout(HCCMD_STANDARD, pHandle, szCmd, NULL, CMDOPT_NONE, APIID_GETCALLFORWARDINGSETTINGS, ParseGetCallForwardingSettings, NULL, hr, 0, dwRetries, dwRetryDelay, NULL, NULL, NULL, pPreFunc)) {
            hr = E_FAIL;
            goto Error;
        }
    }
    else
    {
        (void)TranslateATDCmds(szCmd, MAX_ATCMD_LEN);
        if (!QueueCmdWithTimeout(HCCMD_STANDARD, pHandle, szCmd, NULL, CMDOPT_NONE, APIID_GETCALLFORWARDINGSETTINGS, ParseGetCallForwardingSettings, NULL, hr, 0, dwRetries, dwRetryDelay, NULL, NULL, NULL, pPreFunc)) {
            hr = E_FAIL;
            goto Error;
        }
    }

Error:
    return hr;
}


//
//
//
HRESULT RILDrv_AddCallForwarding(DWORD dwParam, DWORD dwReason, const RILCALLFORWARDINGSETTINGS* lpSettings)
{
    DWORD dwRetries = 0;
    DWORD dwRetryDelay = 0;
    PFN_PRE_CMD_CHECK pPreFunc = NULL;
    UINT i;
    DWORD dwInfoClasses;
    DWORD dwDelayTime = 0;
    BOOL bDelayTime = FALSE;
    UINT nValue = 0;
    DWORD dwClass = 0;
    BYTE bTypeOfAddress;
    char szAddress[MAXLENGTH_ADDRESS];
    char szCmd[SUPSVC_CMDBUF_LENGTH];
    LPSTR szWalk = szCmd;
    HRESULT hr = S_OK;
    BOOL bBasicService = FALSE;
    DWORD dwBasicService = 0;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    // Address and information classes must be specified
    if (!(lpSettings->dwParams & RIL_PARAM_CFS_ADDRESS) ||
        !(lpSettings->dwParams & RIL_PARAM_CFS_INFOCLASSES)) {
        hr = E_INVALIDARG;
        goto Error;
    }

    // Determine the reason value
    for (i = 0; i < NUM_FWDREASONS; i++) {
        if (dwReason == g_rgdwFwdReasons[i]) {
            nValue = i;
            break;
        }
    }
    if (NUM_FWDREASONS == i) {
        hr = E_INVALIDARG;
        goto Error;
    }

    if (GetBasicServiceFromSequenceString(dwReason, bBasicService, dwBasicService)) {
        // Special treatment of Basic Service 10 for unconditional CF: for GCF 27.22.6.2
        // this sequence must be sent do the radio, although this is an illegal BS value.
        if (bBasicService == TRUE && dwBasicService == 10) {
            (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "ATD%s%s\r", g_szSequenceFromDialer, GetATDSemicolon());
            (void)TranslateATDCmds(szCmd, MAX_ATCMD_LEN);
            if (!QueueCmdWithTimeout(HCCMD_STANDARD, pHandle, szCmd, NULL, CMDOPT_NONE, APIID_SETCALLFORWARDINGSTATUS, NULL, NULL, hr, 0, dwRetries, dwRetryDelay, ErrorHandlerForCallForwarding(), NULL, NULL, pPreFunc)) {
                hr = E_FAIL;
            }
            g_szSequenceFromDialer[0] = 0;
            goto Error;
        }
    }

    if (!GetDelayTimeFromSequenceString(dwReason, bDelayTime, dwDelayTime))
    {
        if(lpSettings->dwParams & RIL_PARAM_CFS_DELAYTIME)
        {
            bDelayTime = TRUE;
            dwDelayTime = lpSettings->dwDelayTime;
        }
    }

    // Determine address and type-of-address byte
    hr = RILAddressToString(lpSettings->raAddress, szAddress, MAXLENGTH_ADDRESS, bTypeOfAddress);
    if (FAILED(hr)) {
        goto Error;
    }

    dwInfoClasses = lpSettings->dwInfoClasses;
    if (dwInfoClasses == RIL_INFOCLASS_NONE)
        dwInfoClasses = RIL_INFOCLASS_DEFAULT_CALLFORWARING;

    // In order to pass PTCRB/GCF Testcases: 31.2.1.6.1 & 31.2.1.6.2 , we first try to generate an ATD*#-String,
    // only if this fails, we use AT+CCFC
    if (UseGSM0707Cmd(pHandle) || !RILDrv_GenerateATDString4CCFC(dwReason, dwInfoClasses, szCmd, RIL_FORWARDINGSTATUS_REGISTER, szAddress, dwDelayTime))
    {
        (void)strncpyz(szCmd, "AT", SUPSVC_CMDBUF_LENGTH);
        szWalk = strchr(szCmd, '\0');  // NO_TYPO: 27

        for (i = 0 ; i < NUM_INFOCLASSES; i++) {
            if (dwInfoClasses & g_rgdwInfoClasses[i]) {
                dwClass |= (0x01 << i);
            }
        }

        (void)_snprintfz(szWalk, SUPSVC_CMDBUF_LENGTH - (szWalk - szCmd), "+CCFC=%u,3,\"%s\",%u,%u", nValue, szAddress,
                         bTypeOfAddress, dwClass);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27

        // Add delay time
        if (bDelayTime && ((RIL_FWDREASON_NOREPLY == dwReason) || // GSM 07.07: <time>: when "no reply" is enabled or queried...
                                     (RIL_FWDREASON_ALLFORWARDING == dwReason) ||
                                     (RIL_FWDREASON_ALLCONDITIONAL == dwReason))) {
            (void)_snprintfz(szWalk, SUPSVC_CMDBUF_LENGTH - (szWalk - szCmd), ",,,%u", dwDelayTime);
            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        }

        (void)strncpyz(szWalk, "\r", SUPSVC_CMDBUF_LENGTH - (szWalk - szCmd));  // NO_TYPO: 30
    }

    (void)TranslateATDCmds(szCmd, MAX_ATCMD_LEN);
    if (!QueueCmdWithTimeout(HCCMD_STANDARD, pHandle, szCmd, NULL, CMDOPT_NONE, APIID_ADDCALLFORWARDING, NULL, NULL, hr, 0, dwRetries, dwRetryDelay, ErrorHandlerForCallForwarding(), NULL, NULL, pPreFunc)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}


//
//
//
HRESULT RILDrv_RemoveCallForwarding(DWORD dwParam, DWORD dwReason, DWORD dwInfoClasses)
{
    DWORD dwRetries = 0;
    DWORD dwRetryDelay = 0;
    PFN_PRE_CMD_CHECK pPreFunc = NULL;
    UINT nValue = 0;
    UINT i;
    DWORD dwClass = 0;
    char szCmd[MAX_ATCMD_LEN];
    LPSTR szWalk = szCmd;
    HRESULT hr = S_OK;
    BOOL bBasicService = FALSE;
    DWORD dwBasicService = 0;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    // Determine the reason value
    for (i = 0; i < NUM_FWDREASONS; i++) {
        if (dwReason == g_rgdwFwdReasons[i]) {
            nValue = i;
            break;
        }
    }
    if (NUM_FWDREASONS == i) {
        hr = E_INVALIDARG;
        goto Error;
    }
    if (dwInfoClasses == RIL_INFOCLASS_NONE)
        dwInfoClasses = RIL_INFOCLASS_DEFAULT_CALLFORWARING;

    if (GetBasicServiceFromSequenceString(dwReason, bBasicService, dwBasicService)) {
        // Special treatment of Basic Service 10 for unconditional CF: for GCF 27.22.6.2
        // this sequence must be sent do the radio, although this is an illegal BS value.
        if (bBasicService == TRUE && dwBasicService == 10) {
            (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "ATD%s%s\r", g_szSequenceFromDialer, GetATDSemicolon());
            (void)TranslateATDCmds(szCmd, MAX_ATCMD_LEN);
            if (!QueueCmdWithTimeout(HCCMD_STANDARD, pHandle, szCmd, NULL, CMDOPT_NONE, APIID_SETCALLFORWARDINGSTATUS, NULL, NULL, hr, 0, dwRetries, dwRetryDelay, ErrorHandlerForCallForwarding(), NULL, NULL, pPreFunc)) {
                hr = E_FAIL;
            }
            g_szSequenceFromDialer[0] = 0;
            goto Error;
        }
    }

    // In order to pass PTCRB/GCF Testcases: 31.2.1.6.1 & 31.2.1.6.2, the RIL first tries to generate an ATD*#-String,
    // only if this fails, it uses AT+CCFC. If use of *#-sequneces is disabled, the AT+CCFC is always used.
    if (UseGSM0707Cmd(pHandle) || !RILDrv_GenerateATDString4CCFC(dwReason, dwInfoClasses, szCmd, RIL_FORWARDINGSTATUS_REMOVE, "", 0))
    {
        (void)strncpyz(szCmd, "AT", MAX_ATCMD_LEN);
        szWalk = strchr(szCmd, '\0');  // NO_TYPO: 27

        if ((RIL_INFOCLASS_VOICE | RIL_INFOCLASS_DATA | RIL_INFOCLASS_FAX) == dwInfoClasses) {
            (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "+CCFC=%u,4", nValue);
            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        } else {
            for (i = 0 ; i < NUM_INFOCLASSES; i++) {
                if (dwInfoClasses & g_rgdwInfoClasses[i]) {
                    dwClass |= (0x01 << i);
                }
            }

            (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "+CCFC=%u,4,,,%u", nValue, dwClass);
            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        }
        (void)strncpyz(szWalk, "\r", MAX_ATCMD_LEN - (szWalk - szCmd));  // NO_TYPO: 30
    }

    (void)TranslateATDCmds(szCmd, MAX_ATCMD_LEN);
    if (!QueueCmdWithTimeout(HCCMD_STANDARD, pHandle, szCmd, NULL, CMDOPT_NONE, APIID_REMOVECALLFORWARDING, NULL, NULL, hr, 0, dwRetries, dwRetryDelay, ErrorHandlerForCallForwarding(), NULL, NULL, pPreFunc)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}

//
//
//
HRESULT RILDrv_SetCallForwardingStatus(DWORD dwParam, DWORD dwReason, DWORD dwInfoClasses, DWORD dwStatus)
{
    DWORD dwRetries = 0;
    DWORD dwRetryDelay = 0;
    PFN_PRE_CMD_CHECK pPreFunc = NULL;
    UINT i;
    UINT nValue = 0;
    DWORD dwClass = 0;
    char szCmd[MAX_ATCMD_LEN];
    LPSTR szWalk = szCmd;
    HRESULT hr = S_OK;
    BOOL bBasicService = FALSE;
    DWORD dwBasicService = 0;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }


    // Check thet the stsatus value is in the correct range
    if (RIL_SVCSTAT_DISABLED != dwStatus && RIL_SVCSTAT_ENABLED != dwStatus) {
        hr = E_INVALIDARG;
        goto Error;
    }

    // Determine the reason value
    for (i = 0; i < NUM_FWDREASONS; i++) {
        if (dwReason == g_rgdwFwdReasons[i]) {
            nValue = i;
            break;
        }
    }
    if (NUM_FWDREASONS == i) {
        hr = E_INVALIDARG;
        goto Error;
    }
    if (dwInfoClasses == RIL_INFOCLASS_NONE)
        dwInfoClasses = RIL_INFOCLASS_DEFAULT_CALLFORWARING;

    if (GetBasicServiceFromSequenceString(dwReason, bBasicService, dwBasicService)) {
        // Special treatment of Basic Service 10 for unconditional CF: for GCF 27.22.6.2
        // this sequence must be sent do the radio, although this is an illegal BS value.
        if (bBasicService == TRUE && dwBasicService == 10) {
            (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "ATD%s%s\r", g_szSequenceFromDialer, GetATDSemicolon());
            (void)TranslateATDCmds(szCmd, MAX_ATCMD_LEN);
            if (!QueueCmdWithTimeout(HCCMD_STANDARD, pHandle, szCmd, NULL, CMDOPT_NONE, APIID_SETCALLFORWARDINGSTATUS, NULL, NULL, hr, 0, dwRetries, dwRetryDelay, ErrorHandlerForCallForwarding(), NULL, NULL, pPreFunc)) {
                hr = E_FAIL;
            }
            g_szSequenceFromDialer[0] = 0;
            goto Error;
        }
    }

    // In order to pass PTCRB/GCF Testcases: 31.2.1.6.1 & 31.2.1.6.2, the RIL first tries to generate an ATD*#-String,
    // only if this fails, it uses AT+CCFC. If use of *#-sequneces is disabled, the AT+CCFC is always used.
    if (UseGSM0707Cmd(pHandle) || !RILDrv_GenerateATDString4CCFC(dwReason, dwInfoClasses, szCmd, (RIL_SVCSTAT_DISABLED == dwStatus ? RIL_FORWARDINGSTATUS_DEACTIVATE : RIL_FORWARDINGSTATUS_ACTIVATE), "", 0))
    {
        (void)strncpyz(szCmd, "AT", MAX_ATCMD_LEN);
        szWalk = strchr(szCmd, '\0');  // NO_TYPO: 27

        if ((RIL_INFOCLASS_VOICE | RIL_INFOCLASS_DATA | RIL_INFOCLASS_FAX) == dwInfoClasses) {
            (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "+CCFC=%u,%u", nValue, (RIL_SVCSTAT_DISABLED == dwStatus ? 0 : 1));
            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        } else {
            for (i = 0 ; i < NUM_INFOCLASSES; i++) {
                if (dwInfoClasses & g_rgdwInfoClasses[i]) {
                    dwClass |= (0x01 << i);
                }
            }

            (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "+CCFC=%u,%u,,,%u", nValue,
                             (RIL_SVCSTAT_DISABLED == dwStatus ? 0 : 1), dwClass);
            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        }
        (void)strncpyz(szWalk, "\r", MAX_ATCMD_LEN - (szWalk - szCmd));  // NO_TYPO: 30
    }

    (void)TranslateATDCmds(szCmd, MAX_ATCMD_LEN);
    if (!QueueCmdWithTimeout(HCCMD_STANDARD, pHandle, szCmd, NULL, CMDOPT_NONE, APIID_SETCALLFORWARDINGSTATUS, NULL, NULL, hr, 0, dwRetries, dwRetryDelay, ErrorHandlerForCallForwarding(), NULL, NULL, pPreFunc)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}

// Generation of an ATD-*#-Code instead of AT+CCWA (if possible)
// Parameters: see RILDrv_GetCallWaitingSettings()
// Return Value:
// TRUE - ATD-string created
// FALSE - No corroponding ATD available
static BOOL RILDrv_GenerateATDString4CCWA(DWORD dwInfoClass, LPSTR lpszATDString, DWORD dwStatus)
{
    BOOL bATDavailable = FALSE;
    UINT uiBS;

    // The info class parameter cannot be used for query of the call waiting status.
    if (dwStatus == RIL_CALLWAITINGSTATUS_QUERY)
        uiBS = 100;
    else
        uiBS = RILDrv_FindBS4InfoClass(dwInfoClass);
    if (uiBS > 0)
    {
        UINT i;

        for (i = 0; i < NUM_CALLWAITING_VALUES; i++)
        {
            if (g_rgCallWaitingTypeTable[i].dwCallWaitingStatus == dwStatus)
            {
                char szBS[6];

                if (uiBS >= 100)
                    szBS[0] = '\0';
                else
                    (void)_snprintfz(szBS, 6, "%d", uiBS);

                (void)_snprintfz(lpszATDString, SECUR_CMDBUF_LENGTH, "ATD%s*%s#%s\r", g_rgCallWaitingTypeTable[i].szATD, szBS, GetATDSemicolon());
                bATDavailable = TRUE;
                break;
            }
        }
    }

    if (bATDavailable)
        CleanATDString(lpszATDString);

    return bATDavailable;
}

//
//
//
static HRESULT ParseGetCallWaitingSettings(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    UINT i;
    UINT nStatus;
    UINT nValue;
    DWORD* pdwInfoClasses = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    pdwInfoClasses = (DWORD*)AllocBlob(sizeof(DWORD));
    if (!pdwInfoClasses) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(pdwInfoClasses, 0x00, sizeof(DWORD));

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

    // Parse "+CCWA: "
    while (MatchStringBeginning(szRsp, "+CCWA: ", szRsp)) {
        // Parse "<status>,<class>"
        if (!ParseUInt(szRsp, TRUE, nStatus, szRsp) ||
            !MatchStringBeginning(szRsp, ",", szRsp) ||
            !ParseUInt(szRsp, TRUE, nValue, szRsp)) {
            goto Continue;
        }

        if (1 == nStatus) {
            // Set all required info classes bits
            for (i = 0 ; i < NUM_INFOCLASSES; i++) {
                if (nValue & (0x01 << i)) {
                    *pdwInfoClasses |= g_rgdwInfoClasses[i];
                }
            }
        }

Continue:
        // Find "<postfix>"
        if (!FindRspPostfix(szRsp, szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        // Skip over additional empty line in EHS5.
        if (UNNECESSARY_BLANK_LINES_IN_RESPONSE)
            (void)FindRspPostfix(szRsp, szRsp);
    }

    pBlob = (void*)pdwInfoClasses;
    cbBlob = sizeof(DWORD);

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


//
//
//
HRESULT RILDrv_GetCallWaitingSettings(DWORD dwParam, DWORD dwInfoClasses)
{
    char szCmd[MAX_ATCMD_LEN];
    UINT i;
    DWORD dwClass = 0;

    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    // Default value: all services
    if (dwInfoClasses == RIL_INFOCLASS_NONE)
        dwInfoClasses = RIL_INFOCLASS_DEFAULT_CALLWAITING;

    // In order to pass PTCRB/GCF Testcases: 31.3.1.4 & 31.3.1.5, the RIL first tries to generate an ATD*#-String,
    // only if this fails, it uses AT+CCWA. If use of *#-sequneces is disabled, the AT+CCWA is always used.
    if (UseGSM0707Cmd(pHandle) || !RILDrv_GenerateATDString4CCWA(dwInfoClasses, szCmd, RIL_CALLWAITINGSTATUS_QUERY)) {
        if (dwInfoClasses == (RIL_INFOCLASS_VOICE | RIL_INFOCLASS_DATA | RIL_INFOCLASS_FAX) && !MANDATORY_CLASS_PARAMETER) {
            (void)strncpyz(szCmd, "AT+CCWA=1,2\r", MAX_ATCMD_LEN);
        }
        else {
            for (i = 0 ; i < NUM_INFOCLASSES; i++) {
                if (dwInfoClasses & g_rgdwInfoClasses[i]) {
                    dwClass |= (0x01 << i);
                }
            }

            (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CCWA=1,2,%u\r", dwClass);
        }
    }

    (void)TranslateATDCmds(szCmd, MAX_ATCMD_LEN);
    if (!QueueCmd(HCCMD_STANDARD, pHandle, szCmd, CMDOPT_NONE, APIID_GETCALLWAITINGSETTINGS, ParseGetCallWaitingSettings, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}


//
//
//
HRESULT RILDrv_SetCallWaitingStatus(DWORD dwParam, DWORD dwInfoClasses, DWORD dwStatus)
{
    UINT i;
    DWORD dwClass = 0;
    char szCmd[MAX_ATCMD_LEN];
    LPSTR szWalk = szCmd;
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    // Default value: all services
    if (dwInfoClasses == RIL_INFOCLASS_NONE)
        dwInfoClasses = RIL_INFOCLASS_DEFAULT_CALLWAITING;

    // Check that the status value is in the correct range
    if (RIL_SVCSTAT_DISABLED != dwStatus && RIL_SVCSTAT_ENABLED != dwStatus) {
        hr = E_INVALIDARG;
        goto Error;
    }

    // In order to pass PTCRB/GCF Testcases: 31.3.1.4 & 31.3.1.5, the RIL first tries to generate an ATD*#-String,
    // only if this fails, it uses AT+CCWA. If use of *#-sequneces is disabled, the AT+CCFC is always used.
    if (UseGSM0707Cmd(pHandle) || !RILDrv_GenerateATDString4CCWA(dwInfoClasses, szCmd, RIL_SVCSTAT_DISABLED == dwStatus ? RIL_CALLWAITINGSTATUS_DEACTIVATE:RIL_CALLWAITINGSTATUS_ACTIVATE)) {
        if ((RIL_INFOCLASS_VOICE | RIL_INFOCLASS_DATA | RIL_INFOCLASS_FAX) == dwInfoClasses && !MANDATORY_CLASS_PARAMETER) {
            (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CCWA=1,%u", (RIL_SVCSTAT_DISABLED == dwStatus ? 0 : 1));
            szWalk = strchr(szCmd, '\0');  // NO_TYPO: 27
        }
        else {
            (void)strncpyz(szCmd, "AT", MAX_ATCMD_LEN);
            szWalk += 2;

            for (i = 0 ; i < NUM_INFOCLASSES; i++) {
                if (dwInfoClasses & g_rgdwInfoClasses[i]) {
                    dwClass |= (0x01 << i);
                }
            }

            (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "+CCWA=1,%u,%u", (RIL_SVCSTAT_DISABLED == dwStatus ? 0 : 1),
                                dwClass);
            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        }

        (void) strncpyz(szWalk, "\r", MAX_ATCMD_LEN - (szWalk - szCmd));  // NO_TYPO: 30
    }

    (void)TranslateATDCmds(szCmd, MAX_ATCMD_LEN);
    if (!QueueCmd(HCCMD_STANDARD, pHandle, szCmd, CMDOPT_NONE, APIID_SETCALLWAITINGSTATUS, NULL, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}


//
// Send a USSD string.
//
//  lpbData -- points to a Unicode string
//  dwSize  -- size (in bytes) of Unicode string
//
HRESULT RILDrv_SendSupServiceData(DWORD dwParam, const BYTE* lpbData, DWORD dwSize)
{
    char szCmd[MAX_ATCMD_LEN];
    HRESULT hr = S_OK;
    LPSTR szWalk = szCmd;
    DWORD iPort;
    const char pszCUSDPostfix[] = ",15\r"; // 15 = default alphabet, language unspecified
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }
    if (dwSize > MAX_USSD_STRING_LEN || dwSize == 0) {
        hr = E_INVALIDARG;
        goto Error;
    }

    iPort = pHandle->GetDevice()->GetPortInUSSDAnswerMode();  // Is there a port in the USSD Answer Mode?

    if (iPort == NUMBER_OF_PORTS) {
        DWORD dwLength;
        char szUSSD[MAX_USSD_STRING_LEN*4];
        LPWSTR pwszUSSD = (LPWSTR)lpbData;

        dwLength = dwSize/2;
#ifdef RILDRV_CE6
        if (dwLength > 0) {
            if (pwszUSSD[dwLength-1] == 0)
                dwLength--;
        }
#endif
        (void)UCS2ToGSMHex(pwszUSSD, dwLength, szUSSD, sizeof(szUSSD));
        (void)_snprintfz(szWalk, MAX_ATCMD_LEN, "AT+CUSD=1,\"%s\"", szUSSD);
        szWalk = strchr(szCmd, '\0');

        if ( (szWalk + sizeof(pszCUSDPostfix)) > &szCmd[MAX_ATCMD_LEN])
        {
            hr = E_FAIL;
            goto Error;
        }

        // Append the command postfix after the string we just copied
        (void)strncpyz(szWalk, pszCUSDPostfix, MAX_ATCMD_LEN - (szWalk - szCmd));  // NO_TYPO: 30

        g_dwLastCUSSDCmd = GetTickCount();
        if (!QueueCmd(HCCMD_STANDARD, pHandle, szCmd, CMDOPT_SUPPRESSLOGGING|CMDOPT_LOWESTPRIO, APIID_SENDSUPSERVICEDATA, NULL, NULL, hr)) {
            hr = E_FAIL;
            goto Error;
        }
    }
    else {
        if (dwSize > 0) {
            (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "%S", lpbData);
            pHandle->GetDevice()->LeaveUSSDAnswerMode((COM_PORT_TYPE)iPort, (BYTE *)szCmd, strlen(szCmd));
            // We send a no-operation command to generate an asynchronous response.
            (void)QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_SENDSUPSERVICEDATA, NULL, NULL, hr);
        }
        else {
            pHandle->GetDevice()->CancelUSSDAnswerMode((COM_PORT_TYPE)iPort);
            // We send a no-operation command to generate an asynchronous response.
            (void)QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_SENDSUPSERVICEDATA, NULL, NULL, hr);
        }
    }

Error:
    return hr;
}


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

    // 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, it just means that there is
    // currently no USSD session active. Microsoft expects an 'OK' in
    // this case.
    if (MatchStringAnywhere(szRsp, "ERROR\r", szDummy) || MatchStringAnywhere(szRsp, "+CME ERROR: 3\r\n", szDummy))
    {
        // Since this isn't an error, we reconfigure this responnse to be an 'OK' instead.
        if (pBlob != NULL)
            (void)FreeBlob(pBlob);
        pBlob = NULL;
        cbBlob = 0;
        dwCode = RIL_RESULT_OK;
    }
    return S_OK;
}

//
//
//
HRESULT RILDrv_CancelSupServiceDataSession(DWORD dwParam)
{
    HRESULT hr = S_OK;
    INT iPort;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    iPort = pHandle->GetDevice()->GetPortInUSSDAnswerMode();   // Is there a port in the USSD Answer Mode?

    if (iPort == NUMBER_OF_PORTS) {
        if (!QueueCmd(HCCMD_STANDARD, pHandle, "AT+CUSD=2\r", CMDOPT_NONE, APIID_CANCELSUPSERVICEDATASESSION, NULL, NULL, hr, ParseNoUSSDSession)) {
            hr = E_FAIL;
            goto Error;
        }
    }
    else {
        // In USSD answer mode, we have to send an ESC character directly to the COM port.
        pHandle->GetDevice()->CancelUSSDAnswerMode((COM_PORT_TYPE)iPort);
        // We send a no-operation command to generate an asynchronous response.
        (void)QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_CANCELSUPSERVICEDATASESSION, NULL, NULL, hr);
    }

Error:
    return hr;
}

