/*****************************************************************************
 * Copyright (C) Gemalto M2M GmbH
 *****************************************************************************
 * Project: IMC-RIL-Driver
 *****************************************************************************
 * Author : Wireless Modules RD SW A2
 *****************************************************************************
 * $Workfile: Y:/z_ms-d/src/working/Quinn/RilDrv/data.cpp $
 * $Revision: /main/ms-d/17 $
 *     $Date: 2013-03-21 14:32:19 $
 *   $Author: pmackent $
 *****************************************************************************
 * Target:
 *  Microsoft Windows Mobile 5.0
 *****************************************************************************
 * Description:
 *  Gemalto IMC RIL
 *****************************************************************************
 *
 * $Log: Y:/z_ms-d/src/working/Quinn/RilDrv/data.cpp$ 
 * 
 * /main/ms-d/17   2013-03-21 14:32:19   pmackent 
 * Fix handling of AT+CBST? in CDMA 
 * 
 * /main/ms-d/16   2013-03-20 15:37:06   pmackent 
 * Avoid use of CBST/CRLP in CDMA
 *
 * /main/ms-d/15   2012-10-30 14:36:16   pmackent
 * Fix call status problems (voice and data) in CDMA on WinCE6/7
 *
 * /main/ms-d/14   2012-07-11 15:22:43   pmackent
 * Fix for exception when making a new data connection attempt in CDMA after a failed attempt
 *
 * /main/ms-d/13   2012-04-19 14:42:35   ctna0256
 * Small CDMA adaptions
 *
 * /main/ms-d/12   2012-02-27 10:56:00   ctna0256
 * Handle EHS5 specilities with DTMF, RLP, call control
 *
 * /main/ms-d/11   2011-04-01 11:26:52   ctna0256
 * Adaptions for CSD under CE7
 *
 * /main/ms-d/10   2010-02-24 13:25:52   ctna0256
 * Made CE6 driver comply with radiometrics test
 *
 * /main/ms-d/9   2010-02-03 10:50:48   ctna0256
 * Introduction of WinCE6 support
 *
 * /main/ms-d/8   2009-02-23 13:16:00   ctna0256
 * Avoid unwanted E_FAIL error codes
 *
 * /main/ms-d/7   2008-09-18 15:21:49   ctna0256
 * Utility function to delete trailing commas
 *
 * /main/ms-d/6   2008-08-22 10:23:26   ctna0256
 * Set Cinterion copyright text
 *
 * /main/ms-d/5   2008-08-21 09:34:17   ctna0256
 * Prepared for new disclaimer handling
 *
 * /main/ms-d/4   2007-03-28 12:03:21   mp49210
 * Added new parameters for AT+CBST and AT+CRLP
 *
 * /main/ms-d/3   2007-01-02 13:40:08   mp49210
 * No support of AT^SFNUR in Quinn
 *
 * /main/ms-d/2   2006-11-13 11:40:23   mp49210
 * Use AT+CBST and AT+CRLP only on data port.
 *
 * /main/ms-d/1   2006-10-25 16:53:19   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"

//
// Bearer service names
//
static const DWORD g_rgdwBearerSvcNames[] =
{
    (DWORD)-1, // RIL_BSVCNAME_UNKNOWN
    0,         // RIL_BSVCNAME_DATACIRCUIT_ASYNC_UDI_MODEM
    1,         // RIL_BSVCNAME_DATACIRCUIT_SYNC_UDI_MODEM
    2,         // RIL_BSVCNAME_PADACCESS_ASYNC_UDI
    3,         // RIL_BSVCNAME_PACKETACCESS_SYNC_UDI
    4,         // RIL_BSVCNAME_DATACIRCUIT_ASYNC_RDI
    5,         // RIL_BSVCNAME_DATACIRCUIT_SYNC_RDI
    6,         // RIL_BSVCNAME_PADACCESS_ASYNC_RDI
    7,         // RIL_BSVCNAME_PACKETACCESS_SYNC_RDI
};
#define NUM_BEARERSVCNAMES  (sizeof(g_rgdwBearerSvcNames) / sizeof(DWORD))


//
// Bearer service connection elements
//
static const DWORD g_rgdwBearerSvcConnElems[] =
{
    (DWORD)-1, // RIL_BSVCCE_UNKNOWN
    0,         // RIL_BSVCCE_TRANSPARENT
    1,         // RIL_BSVCCE_NONTRANSPARENT
    2,         // RIL_BSVCCE_BOTH_TRANSPARENT
    3,         // RIL_BSVCCE_BOTH_NONTRANSPARENT
};
#define NUM_BEARERSVCCONNELEMS  (sizeof(g_rgdwBearerSvcConnElems) / sizeof(DWORD))


//
//
//
static HRESULT ParseGetBearerServiceOptions(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    UINT nValue;
    UINT i;
    RILBEARERSVCINFO* prbsi = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

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

    // Parse "<prefix>+CBST: <speed>"
    if (!ParseRspPrefix(szRsp, szRsp)                  ||
        !MatchStringBeginning(szRsp, "+CBST: ", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }
    for (i = 0; i < NUM_CONNSPEEDS; i++)
    {
        if (nValue == g_rgdwConnSpeeds[i])
        {
            prbsi->dwSpeed = i;
            break;
        }
    }
    if (NUM_CONNSPEEDS == i)
    {
        // We couldn't match the response with anything
        prbsi->dwSpeed = RIL_SPEED_UNKNOWN;
    }
    prbsi->dwParams |= RIL_PARAM_BSI_SPEED;

    // Parse ",<svc_name>"
    if (!MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }
    for (i = 0; i < NUM_BEARERSVCNAMES; i++)
    {
        if (nValue == g_rgdwBearerSvcNames[i])
        {
            prbsi->dwServiceName = i;
            break;
        }
    }
    if (NUM_BEARERSVCNAMES == i)
    {
        // We couldn't match the response with anything
        prbsi->dwServiceName = RIL_BSVCNAME_UNKNOWN;
    }
    prbsi->dwParams |= RIL_PARAM_BSI_SERVICENAME;

    // Parse ",<conn_element><postfix>"
    if (!MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp)   ||
        !ParseRspPostfix(szRsp, szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }
    for (i = 0; i < NUM_BEARERSVCCONNELEMS; i++)
    {
        if (nValue == g_rgdwBearerSvcConnElems[i])
        {
            prbsi->dwConnectionElement = i;
            break;
        }
    }
    if (NUM_BEARERSVCCONNELEMS == i)
    {
        // We couldn't match the response with anything
        prbsi->dwConnectionElement = RIL_BSVCCE_UNKNOWN;
    }
    prbsi->dwParams |= RIL_PARAM_BSI_CONNECTIONELEMENT;

    pBlob = (void*)prbsi;
    cbBlob = sizeof(RILBEARERSVCINFO);

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

HRESULT ParseGetBearerServiceOptionsError(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/, DWORD& dwCode)
{
    RILBEARERSVCINFO* prbsi = NULL;

    if (szRsp != NULL) {
        // When the module is in CDMA mode, this option doesn't apply, so just return "OK".
        if (strstr(szRsp, "CME ERROR: 370") != NULL) {
            // Since this isn't an error, we reconfigure this response to be an 'OK' instead (service = "unknown").
            if (pBlob != NULL)
                (void)FreeBlob(pBlob);
            prbsi = (RILBEARERSVCINFO*)AllocBlob(sizeof(RILBEARERSVCINFO));
            memset(prbsi, 0, sizeof(RILBEARERSVCINFO));
            prbsi->cbSize = sizeof(RILBEARERSVCINFO);
            pBlob = (void*)prbsi;
            cbBlob = sizeof(RILBEARERSVCINFO);
            dwCode = RIL_RESULT_OK;
        }
    }
    return S_OK;
}

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

    if (!QueueCmd(HCCMD_DATAPORT_ONLY, pHandle, "AT+CBST?\r", CMDOPT_GENERATE_ERROR_CODE, APIID_GETBEARERSERVICEOPTIONS, ParseGetBearerServiceOptions, NULL, hr, ParseGetBearerServiceOptionsError))
    {
        hr = E_FAIL;
        goto Error;
    }

    Error:
    return hr;
}


//
//
//
HRESULT RILDrv_SetBearerServiceOptions(DWORD dwParam, const RILBEARERSVCINFO* lpBearerServiceInfo)
{
    char szCmd[MAX_ATCMD_LEN];
    LPSTR szWalk = szCmd;
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        hr = E_FAIL;
        goto Error;
    }

    // CDMA: there is no bearer service like on GSM, so the RIL can skip this command.
    if (pHandle->GetDevice()->GetCurrentTechnology() == TEC_MODE_CDMA) {
        if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_SETBEARERSERVICEOPTIONS, NULL, NULL, hr))
            hr = E_FAIL;
        goto Error;
    }

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

    // Add "<speed>"
    if (lpBearerServiceInfo->dwParams & RIL_PARAM_BSI_SPEED)
    {
        DWORD dwSpeed = lpBearerServiceInfo->dwSpeed;

        if (RIL_SPEED_UNKNOWN == dwSpeed)
        {
            hr = E_INVALIDARG;
            goto Error;
        }

        // Not in all networks the automatic speed option works.
        // Since Microsoft UI doesn't always allow to specify a
        // speed, the RIL can use a fixed speed as a default value.
        if (dwSpeed == RIL_SPEED_AUTO) {
            GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("ReplaceBSTautoSpeed"), &dwSpeed);
        }
        if (dwSpeed >= NUM_CONNSPEEDS)
            dwSpeed = RIL_SPEED_AUTO;

        (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "%u", g_rgdwConnSpeeds[dwSpeed]);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    }
    else
    {
        (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "%u", g_rgdwConnSpeeds[RIL_SPEED_9600_V32]);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    }

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

    // Add "<svc_name>"
    if (lpBearerServiceInfo->dwParams & RIL_PARAM_BSI_SERVICENAME)
    {
        if (RIL_BSVCNAME_UNKNOWN == lpBearerServiceInfo->dwServiceName)
        {
            hr = E_INVALIDARG;
            goto Error;
        }

        (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "%u", g_rgdwBearerSvcNames[lpBearerServiceInfo->dwServiceName]);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    }
    else
    {
        (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "%u", g_rgdwBearerSvcNames[RIL_BSVCNAME_DATACIRCUIT_ASYNC_UDI_MODEM]);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    }

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

    // Add "<conn_elem>"
    if (lpBearerServiceInfo->dwParams & RIL_PARAM_BSI_CONNECTIONELEMENT)
    {

        if (RIL_BSVCCE_UNKNOWN == lpBearerServiceInfo->dwConnectionElement)
        {
            hr = E_INVALIDARG;
            goto Error;
        }

        (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "%u",
                         g_rgdwBearerSvcConnElems[lpBearerServiceInfo->dwConnectionElement]);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    }
    else
    {
        (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "%u", g_rgdwBearerSvcConnElems[RIL_BSVCCE_NONTRANSPARENT]);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    }

    // Add "<CR>"
    (void)strncpyz(szWalk, "\r", MAX_ATCMD_LEN - (szWalk - szCmd));
    if (!QueueCmd(HCCMD_DATAPORT_ONLY, pHandle, szCmd, CMDOPT_GENERATE_ERROR_CODE, APIID_SETBEARERSERVICEOPTIONS, NULL, NULL, hr))
    {
        hr = E_FAIL;
        goto Error;
    }

    Error:
    return hr;
}


//
//
//
#ifndef RILDRV_CE7
static HRESULT ParseGetRLPOptions(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    UINT nValue;
    UINT nUsed = 0;
    UINT nAllocated = 0;
    RILRLPINFO* rgrrlpi = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

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

    // Parse "+CRLP: "
    while (MatchStringBeginning(szRsp, "+CRLP: ", szRsp))
    {
        if (nUsed == nAllocated)
        {
            if (!AllocateOrReallocateStorage((BYTE**)&rgrrlpi, sizeof(RILRLPINFO), nUsed, &nAllocated, DATA_ALLOC_NUMBER))
            {
                hr = E_OUTOFMEMORY;
                goto Error;
            }
        }

        memset(&rgrrlpi[nUsed], 0x00, sizeof(RILRLPINFO));
        rgrrlpi[nUsed].cbSize = sizeof(RILRLPINFO);

        // Parse "<iws>"
        if (!ParseUInt(szRsp, TRUE, nValue, szRsp))
        {
            goto Continue;
        }
        rgrrlpi[nUsed].dwIWS = nValue;
        rgrrlpi[nUsed].dwParams |= RIL_PARAM_RLPI_IWS;

        // Parse ",<mws>"
        if (!MatchStringBeginning(szRsp, ",", szRsp) ||
            !ParseUInt(szRsp, TRUE, nValue, szRsp))
        {
            goto Continue;
        }
        rgrrlpi[nUsed].dwMWS = nValue;
        rgrrlpi[nUsed].dwParams |= RIL_PARAM_RLPI_MWS;

        // Parse ",<ack_timer>"
        if (!MatchStringBeginning(szRsp, ",", szRsp) ||
            !ParseUInt(szRsp, TRUE, nValue, szRsp))
        {
            goto Continue;
        }
        rgrrlpi[nUsed].dwAckTimer = nValue;
        rgrrlpi[nUsed].dwParams |= RIL_PARAM_RLPI_ACKTIMER;

        // Parse ",<retr_att>"
        if (!MatchStringBeginning(szRsp, ",", szRsp) ||
            !ParseUInt(szRsp, TRUE, nValue, szRsp))
        {
            goto Continue;
        }
        rgrrlpi[nUsed].dwRetransmissionAttempts = nValue;
        rgrrlpi[nUsed].dwParams |= RIL_PARAM_RLPI_RETRANSMISSIONATTEMPTS;

        // Parse ","
        if (MatchStringBeginning(szRsp, ",", szRsp))
        {
            // Parse "<version>"
            if (!ParseUInt(szRsp, TRUE, nValue, szRsp))
            {
                goto Continue;
            }
            rgrrlpi[nUsed].dwVersion = nValue;
            rgrrlpi[nUsed].dwParams |= RIL_PARAM_RLPI_VERSION;

            // Parse ","
            if (MatchStringBeginning(szRsp, ",", szRsp))
            {
                // Parse "<reseq_period>"
                if (!ParseUInt(szRsp, TRUE, nValue, szRsp))
                {
                    goto Continue;
                }
                rgrrlpi[nUsed].dwResequencingPeriod = nValue;
                rgrrlpi[nUsed].dwParams |= RIL_PARAM_RPLI_RESEQUENCINGPERIOD;
            }
        }

        // If we didn't see the version, assume 0 (per GSM 07.07, ch6.8)
        if (!(rgrrlpi[nUsed].dwParams & RIL_PARAM_RLPI_VERSION))
        {
            rgrrlpi[nUsed].dwVersion = 0;
            rgrrlpi[nUsed].dwParams |= RIL_PARAM_RLPI_VERSION;
        }

        // Increment the array index
        nUsed++;

        Continue:
        // Find "<postfix>"
        if (!FindRspPostfix(szRsp, szRsp))
        {
            hr = E_FAIL;
            goto Error;
        }

#ifdef RILDRV_CE6
        if (nUsed > 0)
        {
            // Special problem of Windwos CE6 Radiometrics test:
            // To pass the radiometrics RLP test case, the RIL must
            // not return information about any other RLP version
            // than 0. So if the RIL found this version, the rest
            // of the output can be skipped.
            if ((rgrrlpi[nUsed-1].dwParams&RIL_PARAM_RLPI_VERSION) != 0 && rgrrlpi[nUsed-1].dwVersion == 0)
            {
                while (MatchStringBeginning(szRsp, "+CRLP: ", szRsp))
                {
                    if (!FindRspPostfix(szRsp, szRsp))
                    {
                        hr = E_FAIL;
                        goto Error;
                    }
                }
            }
        }
#endif
    }

    pBlob = (void*)rgrrlpi;
    cbBlob = nUsed * sizeof(RILRLPINFO);

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

HRESULT ParseGetRLPOptionsError(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/, DWORD& dwCode)
{
    if (szRsp != NULL) {
        // When the module is in CDMA mode, this option doesn't apply, so just return "OK".
        if (strstr(szRsp, "CME ERROR: 370") != NULL) {
            // Since this isn't an error, we reconfigure this response to be an 'OK' instead.
            if (pBlob != NULL)
                (void)FreeBlob(pBlob);
            pBlob = NULL;
            cbBlob = 0;
            dwCode = RIL_RESULT_OK;
        }
    }
    return S_OK;
}
#endif

//
//
//
HRESULT RILDrv_GetRLPOptions(DWORD dwParam)
{
#ifdef RILDRV_CE7
    // The new CE7 connection manager isn't really using this
    // API, so it is best not to execute it.
    return E_NOTIMPL;
#else
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        hr = E_FAIL;
        goto Error;
    }

    if (!QueueCmd(HCCMD_DATAPORT_ONLY, pHandle, "AT+CRLP?\r", CMDOPT_GENERATE_ERROR_CODE, APIID_GETRLPOPTIONS, ParseGetRLPOptions, NULL, hr, ParseGetRLPOptionsError))
    {
        hr = E_FAIL;
        goto Error;
    }

    Error:
    return hr;
#endif
}


//
//
//
HRESULT RILDrv_SetRLPOptions(DWORD dwParam, const RILRLPINFO* lpRlpInfo)
{
    char szCmd[MAX_ATCMD_LEN];
    LPSTR szWalk = szCmd;
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        hr = E_FAIL;
        goto Error;
    }

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

    // Add "<iws>"
    if (lpRlpInfo->dwParams & RIL_PARAM_RLPI_IWS)
    {
        (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "%u", lpRlpInfo->dwIWS);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    }

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

    // Add "<mws>"
    if (lpRlpInfo->dwParams & RIL_PARAM_RLPI_MWS)
    {
        (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "%u", lpRlpInfo->dwMWS);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    }

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

    // Add "<ack_timer>"
    if (lpRlpInfo->dwParams & RIL_PARAM_RLPI_ACKTIMER)
    {
        (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "%u", lpRlpInfo->dwAckTimer);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    }

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

    // Add "<retr_att>"
    if (lpRlpInfo->dwParams & RIL_PARAM_RLPI_RETRANSMISSIONATTEMPTS)
    {
        (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "%u", lpRlpInfo->dwRetransmissionAttempts);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    }

    // Add ","
    (void)strncpyz(szWalk, ",", MAX_PATH - (szWalk - szCmd));
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27

    // Add "<version>"
    if (SUPPORTS_RLP_VERSIONS && (lpRlpInfo->dwParams & RIL_PARAM_RLPI_VERSION))
    {
        // Add "<version>"
        (void)_snprintfz(szWalk, MAX_PATH - (szWalk - szCmd), "%u", lpRlpInfo->dwVersion);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    }

    // Delete trailing commas
    szWalk = DeleteTrailingCommas(szWalk, szCmd);

    // Add "<CR>"
    if (szWalk == NULL)
    {
        hr = E_FAIL;
        goto Error;
    }
    else
        (void)strncpyz(szWalk, "\r", MAX_ATCMD_LEN - (szWalk - szCmd));

    if (!QueueCmd(HCCMD_DATAPORT_ONLY, pHandle, szCmd, CMDOPT_GENERATE_ERROR_CODE, APIID_SETRLPOPTIONS, NULL, NULL, hr))
    {
        hr = E_FAIL;
        goto Error;
    }

    Error:
    return hr;
}

#ifndef RILDRV_CE7
HRESULT ParseGetDataCompression(LPCSTR /*szRsp*/, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    RILDATACOMPINFO* prdci = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

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

    // Just return, that there is no data compression ongoing
    // an give no further information.
    prdci->dwDirection = RIL_DATACOMPDIR_NONE;
    prdci->dwParams = RIL_PARAM_DCI_DIRECTION;

    pBlob = (void*)prdci;
    cbBlob = sizeof(RILDATACOMPINFO);

    Error:
    if (FAILED(hr))
    {
        FreeBlob(prdci);
    }
    return hr;
}
#endif

HRESULT RILDrv_GetDataCompression (DWORD dwParam)
{
#ifdef RILDRV_CE7
    // The new CE7 connection manager isn't really using this
    // API, so it is best not to execute it.
    return E_NOTIMPL;
#else
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        hr = E_FAIL;
        goto Error;
    }

    // The module doesn't actually support this feature, but the Windows CE6
    // connection manager insists on using this API, so the RIL returns as
    // default value, that there is no data compression active.
    if (!QueueCmd(HCCMD_DATAPORT_ONLY, pHandle, NULL, CMDOPT_NOOP, APIID_GETDATACOMPRESSION, ParseGetDataCompression, NULL, hr))
    {
        hr = E_FAIL;
        goto Error;
    }

    Error:
    return hr;
#endif
}

HRESULT RILDrv_SetDataCompression (DWORD dwParam, RILDATACOMPINFO * /*lDComp*/)
{
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        hr = E_FAIL;
        goto Error;
    }
    // The module doesn't actually support this feature, but the Windows CE6
    // connection manager insists on using this API, so the RIL returns "OK"
    // even though nothing gets executed.
    if (!QueueCmd(HCCMD_DATAPORT_ONLY, pHandle, NULL, CMDOPT_NOOP, APIID_SETDATACOMPRESSION, NULL, NULL, hr))
    {
        hr = E_FAIL;
        goto Error;
    }
    Error:
    return hr;
}
