/*****************************************************************************
 * Copyright (C) Gemalto M2M GmbH
 *****************************************************************************
 * Project: IMC-RIL-Driver
 *****************************************************************************
 * Author : Wireless Modules RD SW A2
 *****************************************************************************
 * $Workfile: Y:/z_ms-d/src/working/Quinn/RilDrv/gprs.cpp $
 * $Revision: /main/ms-d/37 $
 *     $Date: 2013-10-24 13:51:14 $
 *   $Author: pmackent $
 *****************************************************************************
 * Target:
 *  Microsoft Windows Mobile 5.0
 *****************************************************************************
 * Description:
 *  Gemalto IMC RIL
 *****************************************************************************
 *
 * $Log: Y:/z_ms-d/src/working/Quinn/RilDrv/gprs.cpp$ 
 * 
 * /main/ms-d/37   2013-10-24 13:51:14   pmackent 
 * Workaround for missing PPP in ATD*99 at AHS3
 *
 * /main/ms-d/36   2013-04-29 16:20:16   pmackent
 * Improved deactivation of data connections
 *
 * /main/ms-d/35   2013-03-26 09:55:05   pmackent
 * Registry key for APN
 *
 * /main/ms-d/34   2012-12-12 15:24:01   pmackent
 * Workarounds for bugs in PXS8 2.821
 *
 * /main/ms-d/33   2012-10-08 16:18:24   pmackent
 * Comply to Klocwork checks.
 *
 * /main/ms-d/32   2012-08-09 13:58:14   pmackent
 * New registry key for plain ATD*99#
 *
 * /main/ms-d/31   2012-08-09 13:46:31   pmackent
 * Fix potential memory leaks
 *
 * /main/ms-d/30   2012-07-18 15:56:05   pmackent
 * New option: use AT+CGDATA instead of ATD*99#
 *
 * /main/ms-d/29   2012-07-06 11:29:42   pmackent
 * Allow PPP traces on CSD/CDMA
 *
 * /main/ms-d/28   2012-04-18 13:46:51   ctna0256
 * Introducing PVS8
 * Implementing CDMA-SMS
 * Various CDMA enhancements
 *
 * /main/ms-d/27   2012-02-22 13:26:39   ctna0256
 * Encapsulate use of Intel AT command set
 *
 * /main/ms-d/26   2012-01-31 16:38:26   ctna0256
 * Added support for EHS5
 *
 * /main/ms-d/25   2011-09-26 13:34:35   ctna0256
 * Comply with CE6 warning level 4
 *
 * /main/ms-d/24   2010-09-03 10:56:58   ctna0256
 * Add GPRS activation notification for CE7
 *
 * /main/ms-d/23   2010-05-07 10:18:28   ctna0256
 * New PPP trace feature needed to find connection problems (IPIS100064425)
 *
 * /main/ms-d/22   2010-04-16 14:23:54   ctna0256
 * Changed GPRS disconnect notification for CE6
 *
 * /main/ms-d/21   2010-03-02 09:45:18   ctna0256
 * wrong check for valid parameters
 *
 * /main/ms-d/20   2010-02-24 13:25:55   ctna0256
 * Made CE6 driver comply with radiometrics test
 *
 * /main/ms-d/19   2010-02-03 10:50:53   ctna0256
 * Introduction of WinCE6 support
 *
 * /main/ms-d/18   2009-06-18 16:17:23   ctna0256
 * Alllow paket data even while initialization
 *
 * /main/ms-d/17   2009-02-23 13:16:02   ctna0256
 * Avoid unwanted E_FAIL error codes
 *
 * /main/ms-d/16   2009-01-16 12:44:30   ctna0450
 * New mechanism to prevent PPP data from slip into the AT response buffer
 *
 * /main/ms-d/15   2008-08-22 10:23:46   ctna0256
 * Set Cinterion copyright text
 *
 * /main/ms-d/14   2008-08-21 09:34:27   ctna0256
 * Prepared for new disclaimer handling
 *
 * /main/ms-d/13   2008-06-20 11:05:41   mp49210
 * Remove unused SIEMENS_MAGNETO define
 *
 * /main/ms-d/12   2008-04-03 14:41:35   mp49210
 * Introduction of new debug zones
 *
 * /main/ms-d/11   2008-02-28 14:41:20   mp49210
 * Handle default values for ATD*99#
 *
 * /main/ms-d/10   2008-01-29 10:25:56   mp49210
 * Block entering GPRS data connection when module is not registered
 *
 * /main/ms-d/9   2007-09-11 15:51:26   mp49210
 * Additional notifications at end of PPP packet data connections
 *
 * /main/ms-d/8   2007-03-28 13:47:57   mp49210
 * Remove 'Classic RIL' specific code and comments
 *
 * /main/ms-d/7   2007-03-26 14:03:19   mp49210
 * Query current context state instead using chached one
 *
 * /main/ms-d/6   2007-02-01 15:27:08   mp49210
 * Change values for GPRS PPP authentication method
 *
 * /main/ms-d/5   2007-01-30 11:55:58   mp49210
 * Registry key for PPP authentication introduced
 *
 * /main/ms-d/4   2007-01-02 15:03:31   mp49210
 * Remove some features not supported by Quinn
 *
 * /main/ms-d/3   2006-12-21 17:21:52   mp49210
 * Use new GPRS Auto Attach method
 *
 * /main/ms-d/2   2006-12-18 17:46:02   mp49210
 * Introduction of AT^SIND=psinfo
 *
 * /main/ms-d/1   2006-10-25 16:53:31   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"
#include "secur.h"


BOOL g_fGPRSActive = FALSE;

// Note: Remember all LISTSTRINGMAP/LISTVALUEMAP structures must be sorted by the string/value key (not the flags key).
// This is because the range parse functions use bsearch to search for the flags fields.

static const LISTSTRINGMAP g_GprsPDPTypes[] =
{
    {"IP",    RIL_GPRSPROTOCOL_IP    },
    {"OSPIH", RIL_GPRSPROTOCOL_IHOSP },
    {"PPP",   RIL_GPRSPROTOCOL_PPP   },
    {"X25",   RIL_GPRSPROTOCOL_X25   },
};
#define NUM_GPRSPDPTYPES     (sizeof(g_GprsPDPTypes) / sizeof(LISTSTRINGMAP))

static const LISTSTRINGMAP g_GprsL2Protocols[] =
{
    {"PPP",   RIL_GPRSL2PROTOCOL_UNKNOWN  },        // Default value, used when GPRSInfoL2ProtocolType parameter in XML is missing or invalid
    {"NULL",  RIL_GPRSL2PROTOCOL_NULL },
    {"PAD",   RIL_GPRSL2PROTOCOL_PAD  },
    {"PPP",   RIL_GPRSL2PROTOCOL_PPP  },
    {"X25",   RIL_GPRSL2PROTOCOL_X25  },
};
#define NUM_GPRSL2PROTOCOLS (sizeof(g_GprsL2Protocols) / sizeof(LISTSTRINGMAP))

#ifdef ACCEPT_ADDITIONAL_CGDCONT_PARAMETERS
static const LISTVALUEMAP g_GprsDataComp[] =
{
    {0, RIL_GPRSDATACOMP_OFF},
    {1, RIL_GPRSDATACOMP_ON}
};
#define NUM_GPRSDATACOMP (sizeof(g_GprsDataComp) / sizeof(LISTVALUEMAP))

static const LISTVALUEMAP g_GprsHeaderComp[] =
{
    {0, RIL_GPRSHEADERCOMP_OFF},
    {1, RIL_GPRSHEADERCOMP_ON}
};
#define NUM_GPRSHEADERCOMP (sizeof(g_GprsHeaderComp) / sizeof(LISTVALUEMAP))
#endif

static const LISTVALUEMAP g_GprsPrecedenceClass[] =
{
    {0, RIL_GPRSPRECEDENCECLASS_SUBSCRIBED },
    {1, RIL_GPRSPRECEDENCECLASS_HIGH       },
    {2, RIL_GPRSPRECEDENCECLASS_NORMAL     },
    {3, RIL_GPRSPRECEDENCECLASS_LOW        }
};
#define NUM_GPRSPRECEDENCECLASS (sizeof(g_GprsPrecedenceClass) / sizeof(LISTVALUEMAP))

static const LISTVALUEMAP g_GprsDelayClass[] =
{
    {0, RIL_GPRSDELAYCLASS_SUBSCRIBED  },
    {1, RIL_GPRSDELAYCLASS_PREDICTIVE1 },
    {2, RIL_GPRSDELAYCLASS_PREDICTIVE2 },
    {3, RIL_GPRSDELAYCLASS_PREDICTIVE3 },
    {4, RIL_GPRSDELAYCLASS_BESTEFFORT  }
};
#define NUM_GPRSDELAYCLASS (sizeof(g_GprsDelayClass) / sizeof(LISTVALUEMAP))

static const LISTVALUEMAP g_GprsReliabilityClass[] =
{
    {0, RIL_GPRSRELIABILITYCLASS_SUBSCRIBED },
    {1, RIL_GPRSRELIABILITYCLASS_1          },
    {2, RIL_GPRSRELIABILITYCLASS_2          },
    {3, RIL_GPRSRELIABILITYCLASS_3          },
    {4, RIL_GPRSRELIABILITYCLASS_4          },
    {5, RIL_GPRSRELIABILITYCLASS_5          }
};
#define NUM_GPRSRELIABILITYCLASS (sizeof(g_GprsReliabilityClass) / sizeof(LISTVALUEMAP))

static const LISTVALUEMAP g_GprsPeakThruClass[] =
{
    {0, RIL_PEAKTHRUCLASS_SUBSCRIBED },
    {1, RIL_PEAKTHRUCLASS_8000       },
    {2, RIL_PEAKTHRUCLASS_16000      },
    {3, RIL_PEAKTHRUCLASS_32000      },
    {4, RIL_PEAKTHRUCLASS_64000      },
    {5, RIL_PEAKTHRUCLASS_128000     },
    {6, RIL_PEAKTHRUCLASS_256000     },
    {7, RIL_PEAKTHRUCLASS_512000     },
    {8, RIL_PEAKTHRUCLASS_1024000    },
    {9, RIL_PEAKTHRUCLASS_2048000    }
};
#define NUM_GPRSPEAKTHRUCLASS (sizeof(g_GprsPeakThruClass) / sizeof(LISTVALUEMAP))

static const LISTVALUEMAP g_GprsMeanThruClass[] =
{
    {0,  RIL_MEANTHRUCLASS_SUBSCRIBED },
    {1,  RIL_MEANTHRUCLASS_100        },
    {2,  RIL_MEANTHRUCLASS_200        },
    {3,  RIL_MEANTHRUCLASS_500        },
    {4,  RIL_MEANTHRUCLASS_1000       },
    {5,  RIL_MEANTHRUCLASS_2000       },
    {6,  RIL_MEANTHRUCLASS_5000       },
    {7,  RIL_MEANTHRUCLASS_10000      },
    {8,  RIL_MEANTHRUCLASS_20000      },
    {9,  RIL_MEANTHRUCLASS_50000      },
    {10, RIL_MEANTHRUCLASS_100000     },
    {11, RIL_MEANTHRUCLASS_200000     },
    {12, RIL_MEANTHRUCLASS_500000     },
    {13, RIL_MEANTHRUCLASS_1000000    },
    {14, RIL_MEANTHRUCLASS_2000000    },
    {15, RIL_MEANTHRUCLASS_5000000    },
    {16, RIL_MEANTHRUCLASS_10000000   },
    {17, RIL_MEANTHRUCLASS_20000000   },
    {18, RIL_MEANTHRUCLASS_50000000   },
    {31, RIL_MEANTHRUCLASS_DONTCARE   }
};
#define NUM_GPRSMEANTHRUCLASS (sizeof(g_GprsMeanThruClass) / sizeof(LISTVALUEMAP))

static const LISTVALUEMAP g_GprsMOSMSService[] =
{
    {0,  RIL_MOSMSSERVICE_GPRS              },
    {1,  RIL_MOSMSSERVICE_CIRCUIT           },
    {2,  RIL_MOSMSSERVICE_GPRSPREFERRED     },
    {3,  RIL_MOSMSSERVICE_CIRCUITPREFERRED  }
};
#define NUM_GPRSMOSMSSERVICE (sizeof(g_GprsMOSMSService) / sizeof(LISTVALUEMAP))

static const DWORD g_GPRSAuthConfig[] =
{
        3,      // none
        1,      // PAP
        0,      // CHAP
        2       // CHAP+PAP
};
#define NUM_GPRSAUTHCONFIG (sizeof(g_GPRSAuthConfig) / sizeof(DWORD))

#define MAX_CONTEXTS    2
static BYTE g_RILGPRSContext[MAX_CONTEXTS*(sizeof(RILGPRSCONTEXT))];
static RILGPRSQOSPROFILE g_RILGPRSQoSMin[MAX_CONTEXTS];
static RILGPRSQOSPROFILE g_RILGPRSQoSReq[MAX_CONTEXTS];
DWORD g_dwCurrentContextID = 0;

#define INVALID_CGACT_VALUE 0xFF
static UINT g_RILCGACT[MAX_CONTEXTS];

// Bug in Magneto AKU2 Cellcore: When executing AT+CGATT? while ATD*99# is in progress the cellcore becomes stuck.
// This is probably caused by the fact, that the answer to AT+CGATT? comes faster that the answer to ATD*99# on Cinterion
// radios, while the radios Microsoft uses for devolpment can give the answer to AT+CGATT? only after ATD*99# finished.
// As a result not only no GPRS data connections are possible, also voice calls cannot be made. Applications outside of the
// Microsoft Cellcore which are using the RIL
// This behaviour has not been found in AKU3.
// If this problem applies, uncomment the following line. This will delay the execution of AT+CGATT? until ATD*99#
// is finished.
//#define PREVENT_CGATT_WHILE_ATD

void ClearGPRSParamCache(void)
{
    int i;

    memset(g_RILGPRSContext, 0, MAX_CONTEXTS*(sizeof(RILGPRSCONTEXT)));
    memset(g_RILGPRSQoSMin, 0, MAX_CONTEXTS*(sizeof(RILGPRSQOSPROFILE)));
    memset(g_RILGPRSQoSReq, 0, MAX_CONTEXTS*(sizeof(RILGPRSQOSPROFILE)));
    for (i = 0; i < MAX_CONTEXTS; i++)
        g_RILCGACT[i] = INVALID_CGACT_VALUE;
}

static HRESULT ParseGetGPRSContextList(LPCSTR /*szRsp*/, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    UINT iNrContexts, i, iWalk;
    HRESULT hr = E_FAIL;
    PBYTE pBuffer = NULL;

    pBlob = NULL;
    cbBlob = 0;

    iNrContexts = 0;
    for (i = 0; i < MAX_CONTEXTS; i++)
    {
        RILGPRSCONTEXT *pContext;

        pContext = (RILGPRSCONTEXT *)(g_RILGPRSContext + (sizeof(RILGPRSCONTEXT) * i));
        if (pContext->cbSize > 0)
            iNrContexts++;
    }

    pBuffer = (PBYTE)ReallocBlob(pBuffer, sizeof(RILGPRSCONTEXT) * iNrContexts);

    if (pBuffer == NULL)
    {
        goto Error;
    }

    iWalk = 0;
    for (i = 0; i < MAX_CONTEXTS; i++)
    {
        RILGPRSCONTEXT *pContext;

        pContext = (RILGPRSCONTEXT *)(g_RILGPRSContext + (sizeof(RILGPRSCONTEXT) * i));
        if (pContext->cbSize > 0)
        {
            memcpy(pBuffer+(sizeof(RILGPRSCONTEXT) * iWalk), pContext, sizeof(RILGPRSCONTEXT));
            iWalk++;
        }
    }

    pBlob = (void*)pBuffer;
    cbBlob = sizeof(RILGPRSCONTEXT) * iNrContexts;
    hr = S_OK;

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

// +CGDCONT?
HRESULT RILDrv_GetGPRSContextList (DWORD dwParam)
{
    HRESULT hr = E_INVALIDARG;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        goto Error;
    }

    if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_GETGPRSCONTEXTLIST, ParseGetGPRSContextList, NULL, hr))
    {
        hr = E_FAIL;
        goto Error;
    }

    Error:
    return hr;
}

static bool queryGPRSPDPContextStatus(CRilInstanceHandle* pHandle){
	DWORD dwPSinfo = 0;
	CRilHandle* pDevice = pHandle->GetDevice();

	if(pDevice == NULL) return false;
	
	(void)QueueInternalCmdBlocking(HCCMD_STANDARD, pDevice, "AT^SIND=psinfo,2\r", CMDOPT_NONE, APIID_GETCURRENTSYSTEMTYPE, 0, 0, 0, ParsePsInfoForCReg, NULL, (LPVOID)&dwPSinfo);
	
	if(dwPSinfo == 16 || dwPSinfo == 17){
		
		return true;
	}

	return false;
}


static HRESULT ParsePDPContextConfigureResult(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;

	// For LTE network, the deault beary will be activated before data connection setup.
	// To this situation, treat the failure as success.
	if(MatchStringAnywhere(szRsp, "+CME ERROR: 148", szDummy)){
		if(pBlob != NULL)
			(void)FreeBlob(pBlob);
		pBlob = NULL;
		cbBlob = 0;
		dwCode = RIL_RESULT_OK;
	}
	return S_OK;
}


HRESULT RILDrv_SetGPRSContext (DWORD dwParam, const RILGPRSCONTEXT* lpGprsContext)
{
    HRESULT hr = E_INVALIDARG;
    char szCmd[MAX_ATCMD_LEN];
    LPSTR szWalk = szCmd;
#ifdef ACCEPT_ADDITIONAL_CGDCONT_PARAMETERS
    UINT Value;
#endif
    WCHAR wszForcedAPN[MAX_ATCMD_LEN];

    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle || lpGprsContext == NULL)
    {
        goto Error;
    }

    // Build command
    (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CGDCONT=%u,", lpGprsContext->dwContextID);
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27

    if (lpGprsContext->dwParams & RIL_PARAM_GCONT_PROTOCOLTYPE)
    {
        LPCSTR szString;
        if (!StringFromFlag(lpGprsContext->dwProtocolType,g_GprsPDPTypes, NUM_GPRSPDPTYPES, szString))
        {
            goto Error;
        }
        (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "\"%s\"",szString);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    }

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


    memset(wszForcedAPN, 0, sizeof(wszForcedAPN));
    if (GetRegistrySZ(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("ForceGPRSAPN"), wszForcedAPN, MAX_ATCMD_LEN))
    {
        // INTERNAL: Bug in Radiometrics test for Windows CE6: the APN cannot be configured
        // INTERNAL: reliable for this test, so it mut be configured via registry.
        (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "\"%S\"", wszForcedAPN);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    }
    else
    {
        if (lpGprsContext->dwParams & RIL_PARAM_GCONT_ACCESSPOINTNAME)
        {
            (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "\"%s\"", AnsiString(lpGprsContext->wszAccessPointName));
            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        }
    }

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

    if (lpGprsContext->dwParams & RIL_PARAM_GCONT_ADDRESS)
    {
        (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "\"%s\"", AnsiString(lpGprsContext->wszAddress));
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    }

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

        if (lpGprsContext->dwParams & RIL_PARAM_GCONT_DATACOMPRESSION)
        {
            if (!ValueFromFlag(lpGprsContext->dwDataCompression,g_GprsDataComp,NUM_GPRSDATACOMP,Value))
            {
                goto Error;
            }
            (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "%u",Value);

            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        }

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

        if (lpGprsContext->dwParams & RIL_PARAM_GCONT_HEADERCOMPRESSION)
        {
            if (!ValueFromFlag(lpGprsContext->dwHeaderCompression,g_GprsHeaderComp,NUM_GPRSHEADERCOMP,Value))
            {
                goto Error;
            }
            (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "%u",Value);

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

            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        }

#ifdef 0
        // No Cinterion module supports this parameter
        if (lpGprsContext->dwParams & RIL_PARAM_GCONT_PARAMETERS)
        {
            (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), ",%s", lpGprsContext->szParameters);
            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        }
#endif
    }
#endif

    // Add "\r"
    (void)strncpyz(szWalk, "\r", MAX_ATCMD_LEN - (szWalk - szCmd));

	if(SUPPORTS_ONLY_DATAMODE && (lpGprsContext->dwContextID ==1) && ((bool)queryGPRSPDPContextStatus(pHandle))){
		 if (!QueueCmd(HCCMD_STANDARD,
	                  pHandle,
	                  NULL,
	                  CMDOPT_NOOP,
	                  APIID_SETGPRSCONTEXT,
	                  NULL,     // Parse fn
	                  NULL,     // Ptr to notification data
	                  hr))
	    {
	        hr = E_FAIL;
	        goto Error;
	    }
	}
	else{
	    if (!QueueCmd(HCCMD_STANDARD,
	                  pHandle,
	                  szCmd,
	                  CMDOPT_GENERATE_ERROR_CODE,
	                  APIID_SETGPRSCONTEXT,
	                  NULL,     // Parse fn
	                  NULL,     // Ptr to notification data
	                  hr))
	    {
	        hr = E_FAIL;
	        goto Error;
	    }
	}

    if (lpGprsContext->dwContextID <= MAX_CONTEXTS && lpGprsContext->dwContextID != 0)
    {
        RILGPRSCONTEXT *pContext;

        memcpy(g_RILGPRSContext + ((lpGprsContext->dwContextID - 1) * sizeof(RILGPRSCONTEXT)), lpGprsContext, sizeof(RILGPRSCONTEXT));
        pContext = (RILGPRSCONTEXT *)(g_RILGPRSContext + ((lpGprsContext->dwContextID - 1) * sizeof(RILGPRSCONTEXT)));
        pContext->cbSize = sizeof(RILGPRSCONTEXT);
        pContext->dwParameterLength = 0;
        pContext->dwParams &= ~RIL_PARAM_GCONT_PARAMETERS;
        memset(&g_RILGPRSQoSMin[lpGprsContext->dwContextID - 1], 0, sizeof(RILGPRSQOSPROFILE));
        memset(&g_RILGPRSQoSReq[lpGprsContext->dwContextID - 1], 0, sizeof(RILGPRSQOSPROFILE));
    }

    Error:
    return hr;
}

// +CGDCONT=<dwContextID>
HRESULT RILDrv_DeleteGPRSContext (DWORD dwParam, DWORD dwContextID)
{
    HRESULT hr = E_INVALIDARG;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        goto Error;
    }

    // Build command
    char szCmd[MAX_ATCMD_LEN];
    (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CGDCONT=%u\r", dwContextID);
    if (!QueueCmd(HCCMD_STANDARD,
                  pHandle,
                  szCmd,
                  CMDOPT_GENERATE_ERROR_CODE,
                  APIID_DELETEGPRSCONTEXT,
                  NULL,     // Parse fn
                  NULL,     // Ptr to notification data
                  hr))
    {
        hr = E_FAIL;
        goto Error;
    }

    if (dwContextID <= MAX_CONTEXTS && dwContextID != 0)
    {
        RILGPRSCONTEXT *pContext;

        pContext = (RILGPRSCONTEXT *)(g_RILGPRSContext + ((dwContextID - 1) * sizeof(RILGPRSCONTEXT)));
        pContext->cbSize = 0;
    }
    Error:
    return hr;
}

HRESULT RILDrv_NdisSetGPRSContextActivated (DWORD dwParam, const RILNDISSETGPRSCONTEXTACTIVATED * /*lpNdisSetGprsContextActivated*/ )
{
    HRESULT hr = E_INVALIDARG;

    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        goto Error;
    }

    // hr = pHandle->NdisSetGPRSContextActivated(lpNdisSetGprsContextActivated);
    hr = E_NOTIMPL;

    Error:
    return hr;
}

#ifndef RILDRV_CE6
HRESULT RILDrv_NdisReceivePacketDone (DWORD dwParam,  RILNDISPACKET* /*lpNdisPacket*/)
{
    HRESULT hr = E_INVALIDARG;

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

    // hr = pHandle->NdisReceivePacketDone(lpNdisPacket);
    hr = E_NOTIMPL;

    Error:
    return hr;
}

HRESULT RILDrv_NdisSendPacket (DWORD dwParam,  RILNDISPACKET* /*lpNdisPacket*/)
{
    HRESULT hr = E_INVALIDARG;

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

    // hr = pHandle->NdisSendPacket(lpNdisPacket);
    hr = E_NOTIMPL;

    Error:
    return hr;
}
#endif

static HRESULT ParseGetRequestedQualityOfServiceList(LPCSTR /*szRsp*/, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    PBYTE pBuffer=NULL;
    UINT i, iNumContext, iIdx;
    HRESULT hr = E_FAIL;

    pBlob = NULL;
    cbBlob = 0;

    iNumContext = 0;
    for (i = 0; i < MAX_CONTEXTS; i++)
        if (g_RILGPRSQoSReq[i].dwContextID == i + 1)
            iNumContext++;

    pBuffer = (PBYTE)ReallocBlob(pBuffer, iNumContext * sizeof(RILGPRSQOSPROFILE));
    if (!pBuffer)
    {
        goto Error;
    }

    iIdx = 0;
    for (i = 0; i < MAX_CONTEXTS; i++)
    {
        if (g_RILGPRSQoSReq[i].dwContextID == i + 1)
        {
            memcpy(pBuffer + (iIdx * sizeof(RILGPRSQOSPROFILE)), &g_RILGPRSQoSReq[i], sizeof(RILGPRSQOSPROFILE));
            iIdx++;
        }
    }

    pBlob = (void*)pBuffer;
    cbBlob = iNumContext * sizeof(RILGPRSQOSPROFILE);
    hr = S_OK;

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

static HRESULT ParseGetMinimumQualityOfServiceList(LPCSTR /*szRsp*/, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    PBYTE pBuffer=NULL;
    UINT i, iNumContext, iIdx;
    HRESULT hr = E_FAIL;

    pBlob = NULL;
    cbBlob = 0;

    iNumContext = 0;
    for (i = 0; i < MAX_CONTEXTS; i++)
        if (g_RILGPRSQoSMin[i].dwContextID == i + 1)
            iNumContext++;

    pBuffer = (PBYTE)ReallocBlob(pBuffer, iNumContext * sizeof(RILGPRSQOSPROFILE));
    if (!pBuffer)
    {
        goto Error;
    }

    iIdx = 0;
    for (i = 0; i < MAX_CONTEXTS; i++)
    {
        if (g_RILGPRSQoSMin[i].dwContextID == i + 1)
        {
            memcpy(pBuffer + (iIdx * sizeof(RILGPRSQOSPROFILE)), &g_RILGPRSQoSMin[i], sizeof(RILGPRSQOSPROFILE));
            iIdx++;
        }
    }

    pBlob = (void*)pBuffer;
    cbBlob = iNumContext * sizeof(RILGPRSQOSPROFILE);
    hr = S_OK;

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

//
//
//
HRESULT RILDrv_GetRequestedQualityOfServiceList (DWORD dwParam)
{
    HRESULT hr = E_INVALIDARG;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        goto Error;
    }

    if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_GETREQUESTEDQUALITYOFSERVICELIST, ParseGetRequestedQualityOfServiceList, NULL, hr))
    {
        hr = E_FAIL;
        goto Error;
    }

    Error:
    return hr;
}

//
//
//
HRESULT RILDrv_GetMinimumQualityOfServiceList (DWORD dwParam)
{
    HRESULT hr = E_INVALIDARG;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        goto Error;
    }

    if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_GETMINIMUMQUALITYOFSERVICELIST, ParseGetMinimumQualityOfServiceList, NULL, hr))
    {
        hr = E_FAIL;
        goto Error;
    }

    Error:
    return hr;
}

// +CGQREQ=[<cid> [,<precedence > [,<delay> [,<reliability.> [,<peak> [,<mean>]]]]]]
// +CGQMIN=[<cid> [,<precedence > [,<delay> [,<reliability.> [,<peak> [,<mean>]]]]]]
/*
typedef struct rilgprsqosprofile_tag {
    DWORD cbSize;                       // @field structure size in bytes
    DWORD dwParams;                     // @field indicates valid parameters
    DWORD dwContextID;                  // @field the context number
    DWORD dwPrecedenceClass;            // @field a RIL_GPRSPRECEDENCECLASS_* constant
    DWORD dwDelayClass;                 // @field a RIL_GPRSDELAYCLASS_* constant
    WCHAR dwReliabilityClass;           // @field a RIL_GPRSRELIABILITYCLASS_* constant
    DWORD dwPeakThruClass;              // @field a RIL_GPRSPEAKTHRUCLASS_* constant
    DWORD dwMeanThruClass;              // @field a RIL_GPRSMEANTHRUCLASS_* constant
} RILGPRSQOSPROFILE, *LPRILGPRSQOSPROFILE;
*/
static HRESULT SetQOS(DWORD dwParam, const RILGPRSQOSPROFILE* lpGprsQosProfile, LPCSTR szATCmd, const APIID apiid)
{
    HRESULT hr = E_INVALIDARG;
    char szCmd[MAX_ATCMD_LEN];
    LPSTR szWalk = szCmd;
    UINT Value;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);

    if (!pHandle)
    {
        goto Error;
    }

    // Build command
    (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT%s=%u,", szATCmd, lpGprsQosProfile->dwContextID);
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27

    if (lpGprsQosProfile->dwParams & RIL_PARAM_GQOSP_PRECEDENCECLASS)
    {
        if (!ValueFromFlag(lpGprsQosProfile->dwPrecedenceClass,g_GprsPrecedenceClass,NUM_GPRSPRECEDENCECLASS,Value))
        {
            goto Error;
        }
        (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "%u",Value);

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

        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    }

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

    if (lpGprsQosProfile->dwParams & RIL_PARAM_GQOSP_DELAYCLASS)
    {
        if (!ValueFromFlag(lpGprsQosProfile->dwDelayClass,g_GprsDelayClass,NUM_GPRSDELAYCLASS,Value))
        {
            goto Error;
        }
        (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "%u",Value);

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

        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    }

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

    if (lpGprsQosProfile->dwParams & RIL_PARAM_GQOSP_RELIABILITYCLASS)
    {
        if (!ValueFromFlag(lpGprsQosProfile->dwReliabilityClass,g_GprsReliabilityClass,NUM_GPRSRELIABILITYCLASS,Value))
        {
            goto Error;
        }
        (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "%u",Value);

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

        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    }

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

    if (lpGprsQosProfile->dwParams & RIL_PARAM_GQOSP_PEAKTHRUCLASS)
    {
        if (!ValueFromFlag(lpGprsQosProfile->dwPeakThruClass,g_GprsPeakThruClass,NUM_GPRSPEAKTHRUCLASS,Value))
        {
            goto Error;
        }
        (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "%u",Value);

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

        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    }

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

    if (lpGprsQosProfile->dwParams & RIL_PARAM_GQOSP_MEANTHRUCLASS)
    {
        if (!ValueFromFlag(lpGprsQosProfile->dwMeanThruClass,g_GprsMeanThruClass,NUM_GPRSMEANTHRUCLASS,Value))
        {
            goto Error;
        }
        (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "%u",Value);

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

        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    }

    // Add "\r"
    (void)strncpyz(szWalk, "\r", MAX_ATCMD_LEN - (szWalk - szCmd));

    if (!QueueCmd(HCCMD_STANDARD,
                  pHandle,
                  szCmd,
                  CMDOPT_GENERATE_ERROR_CODE,
                  apiid,
                  NULL,     // Parse fn
                  NULL,     // Ptr to notification data
                  hr))
    {
        hr = E_FAIL;
        goto Error;
    }

    if (apiid == APIID_SETMINIMUMQUALITYOFSERVICE && lpGprsQosProfile->dwContextID <= MAX_CONTEXTS && lpGprsQosProfile->dwContextID != 0)
        memcpy(&g_RILGPRSQoSMin[lpGprsQosProfile->dwContextID - 1], lpGprsQosProfile, sizeof(RILGPRSQOSPROFILE));
    if (apiid == APIID_SETREQUESTEDQUALITYOFSERVICE && lpGprsQosProfile->dwContextID <= MAX_CONTEXTS && lpGprsQosProfile->dwContextID != 0)
        memcpy(&g_RILGPRSQoSReq[lpGprsQosProfile->dwContextID - 1], lpGprsQosProfile, sizeof(RILGPRSQOSPROFILE));

    Error:
    return hr;
}

// +CGQREQ=<params>
HRESULT RILDrv_SetRequestedQualityOfService (DWORD dwParam, const RILGPRSQOSPROFILE* lpGprsQosProfile)
{
    return SetQOS(dwParam,lpGprsQosProfile, "+CGQREQ",APIID_SETREQUESTEDQUALITYOFSERVICE);
}

// +CGQMIN=<params>
HRESULT RILDrv_SetMinimumQualityOfService (DWORD dwParam, const RILGPRSQOSPROFILE* lpGprsQosProfile)
{
    return SetQOS(dwParam,lpGprsQosProfile, "+CGQMIN",APIID_SETMINIMUMQUALITYOFSERVICE);
}

static HRESULT DeleteQOS(DWORD dwParam, DWORD dwContextID, LPCSTR szATCmd, const APIID apiid)
{
    HRESULT hr = E_INVALIDARG;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);

    if (!pHandle)
    {
        goto Error;
    }

    // Build command
    char szCmd[MAX_ATCMD_LEN];
    (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT%s=%u\r", szATCmd, dwContextID);
    if (!QueueCmd(HCCMD_STANDARD,
                  pHandle,
                  szCmd,
                  CMDOPT_GENERATE_ERROR_CODE,
                  apiid,
                  NULL,  // Parse fn
                  NULL,     // Ptr to notification data
                  hr))
    {
        hr = E_FAIL;
        goto Error;
    }

    if (apiid == APIID_DELETEMINIMUMQUALITYOFSERVICE && dwContextID <= MAX_CONTEXTS && dwContextID != 0)
        memset(&g_RILGPRSQoSMin[dwContextID - 1], 0, sizeof(RILGPRSQOSPROFILE));
    if (apiid == APIID_DELETEREQUESTEDQUALITYOFSERVICE && dwContextID <= MAX_CONTEXTS && dwContextID != 0)
        memset(&g_RILGPRSQoSReq[dwContextID - 1], 0, sizeof(RILGPRSQOSPROFILE));

    Error:
    return hr;
}

// +CGQREQ=<dwContextID>
HRESULT RILDrv_DeleteRequestedQualityOfService (DWORD dwParam, DWORD dwContextID)
{
    return DeleteQOS(dwParam, dwContextID, "+CGQREQ", APIID_DELETEREQUESTEDQUALITYOFSERVICE);
}

// +CGQMIN=<dwContextID)
HRESULT RILDrv_DeleteMinimumQualityOfService (DWORD dwParam, DWORD dwContextID)
{
    return DeleteQOS(dwParam, dwContextID, "+CGQMIN", APIID_DELETEMINIMUMQUALITYOFSERVICE);
}

// +CGATT=[0|1]
HRESULT RILDrv_SetGPRSAttached (DWORD dwParam, BOOL fAttached)
{
    HRESULT hr = E_INVALIDARG;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        goto Error;
    }

    // Build command
    char szCmd[MAX_ATCMD_LEN];
    (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CGATT=%u\r", fAttached ? 1 : 0);

    if (!QueueCmd(HCCMD_STANDARD,
                  pHandle,
                  szCmd,
                  CMDOPT_GENERATE_ERROR_CODE,
                  APIID_SETGPRSATTACHED,
                  NULL,  // Parse fn
                  NULL,     // Ptr to notification data
                  hr))
    {
        hr = E_FAIL;
        goto Error;
    }

    Error:
    return hr;
}

/*
+CGATT: <state>
<state>: indicates the state of GPRS attachment
0 - detached
1 - attached
*/
static HRESULT ParseGetGPRSAttached(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    UINT nValue, nSum;
    HRESULT hr = E_FAIL;
    BOOL *pbResult = NULL;

    pBlob = NULL;
    cbBlob = 0;

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

    // Parse "+CGATT: "
    if (!MatchStringBeginning(szRsp, "+CGATT: ", szRsp))
    {
        goto Error;
    }

    // Parse <state>
    if (!ParseUInt(szRsp, TRUE, nValue, szRsp))
    {
        goto Error;
    }

    pbResult = (BOOL*)AllocBlob(sizeof(BOOL));
    if (!pbResult)
    {
        goto Error;
    }
    *pbResult = (BOOL)nValue;
    pBlob = (void*)pbResult;
    cbBlob = sizeof(BOOL);
    hr = S_OK;

    // Now there might follow the output of the AT^SGACT command. If not, we can assume,
    // that the status returned by AT+CGATT? is correct.

    // Parse "<postfix><prefix>"
    if (!ParseRspPostfix(szRsp, szRsp) || !ParseRspPrefix(szRsp, szRsp))
    {
        goto Error;
    }

    // Parse "+CGATT: "
    if (!MatchStringBeginning(szRsp, "^SGACT: ", szRsp))
    {
        goto Error;
    }

    // Parse <sum>
    if (!ParseUInt(szRsp, TRUE, nSum, szRsp))
    {
        goto Error;
    }

    // If AT+CGATT? says "0", but AT^SCGACT says "1" or "2", the value of AT+CGATT? is supposed to be "1".
    if (nValue == 0 && nSum > 0)
        *pbResult = TRUE;

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

    return hr;
}

#ifdef PREVENT_CGATT_WHILE_ATD
static HRESULT PreInitCGATT(CRilHandle * pHandle, COM_PORT_TYPE /*iPort*/)
{
    CCommand *pCmd;
    HRESULT hr = S_OK;
    COM_PORT_TYPE  iPort = DATA_PORT;

    while (iPort != INVALID_PORT)
    {
        pCmd = pHandle->GetCurrentCommand();
        if (pCmd != NULL) {
            char *szCmd;

            szCmd = pCmd->GetCmd();
            if (szCmd != NULL) {
                if (strncmp(szCmd, "ATD*99",6) == 0) {
                    hr = E_FAIL;
                    break;
                }
            }
        }
        if (iPort == DATA_PORT)
            iPort = g_ComManager.GetAuxDataPort();
        else
            iPort = INVALID_PORT;
    }

    return hr;
}
#endif

// +CGATT?
HRESULT RILDrv_GetGPRSAttached (DWORD dwParam)
{
    HRESULT hr = E_INVALIDARG;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        goto Error;
    }

    // Some modules might return +CGATT: 0, although there are GPRS contexts active. On these
    // modules we also have to check AT^SGACT to find out about the number of active contexts.
#ifdef PREVENT_CGATT_WHILE_ATD
    if (!QueueCmdWithTimeout(HCCMD_STANDARD,
                  pHandle,
                  "AT+CGATT?\r", NULL,
                  CMDOPT_GENERATE_ERROR_CODE,
                  APIID_GETGPRSATTACHED,
                  ParseGetGPRSAttached,  // Parse fn
                  NULL,     // Ptr to notification data
                  hr,
                  0,100,500, NULL, NULL, NULL, PreInitCGATT))
#else
    if (!QueueCmd(HCCMD_STANDARD,
                  pHandle,
                  "AT+CGATT?\r",
                  CMDOPT_GENERATE_ERROR_CODE,
                  APIID_GETGPRSATTACHED,
                  ParseGetGPRSAttached,  // Parse fn
                  NULL,     // Ptr to notification data
                  hr))
#endif
    {
        hr = E_FAIL;
        goto Error;
    }

    Error:
    return hr;
}

// +CGACT=[0|1],<dwContextID>[,<dwContextID>...]
HRESULT RILDrv_SetGPRSContextActivated (DWORD dwParam, DWORD dwContextID, BOOL fContextActivation)
{
    HRESULT              hr = E_INVALIDARG;
    CNotificationData*   pnd = NULL;
    CRilInstanceHandle*  pHandle = ExtractHandle(dwParam);
    CComHandle*          pDataPort = NULL;
    COM_PORT_TYPE        iPort;
	bool				 bSetPDP = true;

    iPort = g_ComManager.GetPortFromContextId(dwContextID);

    if (pHandle != NULL)
        pDataPort = pHandle->GetDevice()->GetComDevice(iPort);

    if (!pHandle || !pDataPort || iPort == INVALID_PORT)
    {
        goto Error;
    }

    // Switch modem port back to cammand mode (if not already happend).
    (void)pDataPort->SwitchBackToCommandMode();

    // Build command
    char szCmd[MAX_ATCMD_LEN];

    if(fContextActivation)
    {
        g_fGPRSActive = TRUE;
        if (dwContextID > 0 && dwContextID < MAX_CONTEXTS)
            g_RILCGACT[dwContextID-1] = 1;

        (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CGACT=1,%u\r", dwContextID);
    }
    else
    {
        BOOL bAnyContextActive;
        INT i;

        if (dwContextID > 0 && dwContextID < MAX_CONTEXTS)
            g_RILCGACT[dwContextID-1] = 0;

        bAnyContextActive = FALSE;
        for (i = 0; i < MAX_CONTEXTS; i++)
        {
            if (g_RILCGACT[i] == 1)
                bAnyContextActive = TRUE;
        }

        g_fGPRSActive = bAnyContextActive;

        // Read the status
        if (!QueueInternalCmd(HCCMD_STANDARD, pHandle->GetDevice(), "AT+CGACT?\r", CMDOPT_NONE, APIID_SETGPRSCONTEXTACTIVATED, 0, 0, 0))
        {
            hr=E_FAIL;
            goto Error;
        }

		//psInfo check for Boxwood LTE
		if(SUPPORTS_ONLY_DATAMODE && (dwContextID == 1) && ((bool)queryGPRSPDPContextStatus(pHandle))){
			bSetPDP = false;
		}
		else{
        	// Deactivate the context
        	(void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CGACT=0,%u\r", dwContextID);
		}
    }

    if (!fContextActivation && bSetPDP) {
        // Create the notification data in case we are deactivating.
        pnd = new CNotificationData;
        if (pnd)
        {
            RILGPRSCONTEXTACTIVATED GprsContext;
            GprsContext.cbSize = sizeof(GprsContext);
            GprsContext.dwContextID = dwContextID;
            GprsContext.dwEvent = RIL_RILGPRSCONTEXTACTIVATED_MEDEACT;
            GprsContext.fActivated = FALSE;
            if (!pnd->InitFromRealBlob(RIL_NOTIFY_GPRSCONNECTIONSTATUS, &GprsContext, sizeof(GprsContext))) {
                delete pnd;
                pnd = NULL;
            }
        }
        (void)QueueInternalCmd(HCCMD_STANDARD, pHandle->GetDevice(), szCmd, CMDOPT_NONE, APIID_SETGPRSCONTEXTACTIVATED, 0, 2, 2000);
    }

    if (g_dwRegStatus != RIL_REGSTAT_HOME && g_dwRegStatus != RIL_REGSTAT_ROAMING)
    {
        // Potential problem: if we send ATD*99 while the module is searching for a network,
        // the AT instance may becomes unavailable and will accept no further AT commands.
        if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_ENTERGPRSDATAMODE, ParseForceFailureResponse, NULL, hr, NULL, (LPVOID)RIL_E_NETWKTEMPFAILURE)) {
            hr = E_FAIL;
            goto Error;
        }
    }
    else
    {
        if (!QueueCmd(HCCMD_STANDARD,
                      pHandle,
                      NULL,
                      CMDOPT_NOOP,
                      APIID_SETGPRSCONTEXTACTIVATED,
                      NULL,  // Parse fn
                      pnd,     // Ptr to notification data
                      hr))
        {
            hr = E_FAIL;
            goto Error;
        }
    }

    Error:
    return hr;
}

/*
+CGACT: <cid>, <state>
[<CR><LF>+CGACT: <cid>, <state>
[...]]
*/
static HRESULT ParseGetGPRSContextActivatedList(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    PBYTE pBuffer=NULL;
    DWORD dwBufferSize=0;
    RILGPRSCONTEXTACTIVATED *pCtxtAct;
    UINT nValue;
    HRESULT hr = E_FAIL;
    BOOL bSuccess;

    pBlob = NULL;
    cbBlob = 0;

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

    // Parse "+CGACT: "
    while (MatchStringBeginning(szRsp, "+CGACT: ", szRsp))
    {
        // Reallocate our buffer for the additional data
        pBuffer = (PBYTE)ReallocBlob(pBuffer,dwBufferSize+sizeof(RILGPRSCONTEXTACTIVATED));
        if (!pBuffer)
        {
            goto Error;
        }

        pCtxtAct = (RILGPRSCONTEXTACTIVATED *)(pBuffer+dwBufferSize);
        memset(pCtxtAct, 0x00, sizeof(RILGPRSCONTEXTACTIVATED));
        pCtxtAct->cbSize=sizeof(RILGPRSCONTEXTACTIVATED);

        dwBufferSize+=sizeof(RILGPRSCONTEXTACTIVATED);

        // Parse <cid>
        if (!ParseUInt(szRsp, TRUE, nValue, szRsp))
        {
            goto Continue;
        }

        pCtxtAct->dwContextID = nValue;

        // Parse ,<fActivated>
        if (!ParseRspParamSeparator(szRsp, szRsp) ||
            (!ParseUInt(szRsp, TRUE, nValue, szRsp)) )
        {
            goto Continue;
        }

        pCtxtAct->fActivated = (BOOL)nValue;
        if ((UINT)pParam < MAX_CONTEXTS)
             g_RILCGACT[(UINT)pParam] = pCtxtAct->fActivated;

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

    pBlob = (void*)pBuffer;
    cbBlob = dwBufferSize;
    hr = S_OK;

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

// +CGACT?
HRESULT RILDrv_GetGPRSContextActivatedList (DWORD dwParam)
{
    HRESULT hr = E_INVALIDARG;

    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        goto Error;
    }

    if (!QueueCmd(HCCMD_URCPORT_ONLY,
                  pHandle,
                  "AT+CGACT?\r",
                  CMDOPT_GENERATE_ERROR_CODE,
                  APIID_GETGPRSCONTEXTACTIVATEDLIST,
                  ParseGetGPRSContextActivatedList,  // Parse fn
                  NULL,     // Ptr to notification data
                  hr))
    {
        hr = E_FAIL;
        goto Error;
    }

    Error:
    return hr;
}

#ifdef RILDRV_CE7
void SendGPRSActivatedNotification(CRilHandle* pRilDevice, DWORD dwContextID)
{
    RILGPRSCONTEXTACTIVATED rGPRSca;
    CNotificationData* pnd = NULL;

    DWORD dwCode = RIL_NOTIFY_GPRSCONNECTIONSTATUS;

    memset(&rGPRSca, 0, sizeof(RILGPRSCONTEXTACTIVATED));
    rGPRSca.cbSize = sizeof(RILGPRSCONTEXTACTIVATED);
    rGPRSca.dwContextID = dwContextID;
    rGPRSca.dwEvent = RIL_RILGPRSCONTEXTACTIVATED_MEACT;
    rGPRSca.fActivated = TRUE;
    pnd = new CNotificationData;
    if (pnd)
    {
        if (pnd->InitFromRealBlob(dwCode, (void*)&rGPRSca, rGPRSca.cbSize))
        {
            pRilDevice->BroadcastNotification(pnd);     // this function will delete the pnd object
        }
        else
        {
            delete pnd;
        }
    }
}
#endif // RILDRV_CE7

void SendGPRSDeactivatedNotification(CRilHandle* pRilDevice, DWORD dwContextID)
{
#ifndef RILDRV_CE6
    DWORD dwCode = RIL_NOTIFY_GPRS_DISCONNECT;
#else
    DWORD dwCode = RIL_NOTIFY_GPRSCONNECTIONSTATUS;
#endif
    RILGPRSCONTEXTACTIVATED rGPRSca;
    CNotificationData* pnd = NULL;

    memset(&rGPRSca, 0, sizeof(RILGPRSCONTEXTACTIVATED));
    rGPRSca.cbSize = sizeof(RILGPRSCONTEXTACTIVATED);
    rGPRSca.dwContextID = dwContextID;
    rGPRSca.dwEvent = RIL_RILGPRSCONTEXTACTIVATED_NWDEACT;  // since the +CGEV: URC is not available, we have to guess here
    rGPRSca.fActivated = FALSE;
    pnd = new CNotificationData;
    if (pnd)
    {
        if (pnd->InitFromRealBlob(dwCode, (void*)&rGPRSca, rGPRSca.cbSize))
            pRilDevice->BroadcastNotification(pnd);     // this function will delete the pnd object
        else
            delete pnd;
    }
}

HRESULT ParseForceFailureResponse(LPCSTR, void*&, UINT&, LPVOID pParam)
{
    // Force a failure
    return (HRESULT)pParam;
}

//
// This function will be called, if an ATD*99 command ended. Since the Microsoft Connection
// Manager assumes, that a GPRS context had been activated, this function will send the
// notification about the end of the context.
//
static HRESULT ParseEnterGPRSDataModeError(LPCSTR szRsp, void*& /*pBlob*/, UINT& /*cbBlob*/,LPVOID /*pParam*/, DWORD& /*dwCode*/)
{
    // Although this is the error parser, this will also be called on CONNECT, so
    // the response has to be checked. If it is NULL, the module shut down
    // during GPRS attempt, this doesn't need to be handled.
    if (szRsp == NULL)
        goto Error;
    if (strstr(szRsp,"CONNECT") != NULL)
        goto Error;
    // Check for valid context ID and port type
    if (g_dwCurrentContextID == 0)
        goto Error;

    SendGPRSDeactivatedNotification(g_ComManager.GetRILDevice(), g_dwCurrentContextID);

Error:
    return S_OK;
}


// +CGDATA=<params>
HRESULT RILDrv_EnterGPRSDataMode (DWORD dwParam, const RILENTERGPRSDATAMODE * lpEnterGprsDataMode)
{
    HRESULT hr = E_INVALIDARG;
    char szCmd[MAX_ATCMD_LEN];
    LPSTR szWalk = szCmd;
    DWORD dwOptions = CMDOPT_DIALDATA;
    PFN_CMD_PARSE pfnParse = NULL;
    COM_PORT_TYPE iPort;
    INT iContext;
    UINT uiCGACTCache;
    DWORD dwAuth = 0;
    DWORD dwReg = 0;

    (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("UseCGDATA"), &dwReg);

    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        goto Error;
    }

    if (lpEnterGprsDataMode) {
        if (lpEnterGprsDataMode->dwNumContexts != 1)
        {
            RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: Tried to open %u contexts at the same time.\r\n"), lpEnterGprsDataMode->dwNumContexts));
            hr = E_INVALIDARG;
            goto Error;
        }
        iContext = (INT)lpEnterGprsDataMode->dwContextID[0];
    }
    else {
        iContext = 1;
    }
    iPort = g_ComManager.GetPortFromContextId(iContext);

    if (iContext > MAX_CONTEXTS) {
        hr = E_INVALIDARG;
        goto Error;
    }
    if (iPort == INVALID_PORT) {
        goto Error;
    }

    if (dwReg == 1) {
        if (lpEnterGprsDataMode) {
            LPCSTR szString;

            // Use AT+CGDATA, this won't work in all networks, but isn't effected by the
            // limitations from the fixed dialing.
            if (!StringFromFlag(lpEnterGprsDataMode->dwL2Protocol,g_GprsL2Protocols, NUM_GPRSL2PROTOCOLS,szString))
            {
                goto Error;
            }
            (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CGDATA=\"%s\",%d\r", szString, lpEnterGprsDataMode->dwContextID[0]);
        }
        else {
            // Default: use context 1 with PPP.
            (void)strncpyz(szCmd, "AT+CGDATA=\"PPP\",1\r", MAX_ATCMD_LEN);
        }
    }
    else {
        // Send "ATD*99#" instead of "AT+CDATA" because the former is tested by OEM1 and acts in the same manner as far as we're concerned
        // Parameters to RILDrv_EnterGPRSDataMode() may be ignored or arguments can be supplied as part of the dial string instead
        (void)strncpyz(szCmd, "ATD*99", MAX_ATCMD_LEN);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27

        if (!GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("UsePlainATD99"), &dwReg))
            dwReg = 0;

        if (lpEnterGprsDataMode && dwReg != 1)
        {
            (void)strncpyz(szWalk, "**", MAX_ATCMD_LEN - (szWalk - szCmd));
            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27

            // Build command
            LPCSTR szString;
            if (!StringFromFlag(lpEnterGprsDataMode->dwL2Protocol,g_GprsL2Protocols, NUM_GPRSL2PROTOCOLS,szString))
            {
                goto Error;
            }
            if (INCORRECT_L2P_PARAM && strcmp(szString, "PPP") == 0)
                (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "%s*","1");  // Bug: "PPP" must be replaced by "1" in the AHS3
            else
                (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "%s*",szString);
            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27

            for (DWORD i=0;i<lpEnterGprsDataMode->dwNumContexts;i++)
            {
                (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "%u#", lpEnterGprsDataMode->dwContextID[i]);
                szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
            }
        }
        else {
            (void)strncpyz(szWalk, "#", MAX_ATCMD_LEN - (szWalk - szCmd));
            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        }

        // Add "\r"
        (void)strncpyz(szWalk, "\r", MAX_ATCMD_LEN - (szWalk - szCmd));
    }

    uiCGACTCache = g_RILCGACT[iContext-1];
	(void)QueueInternalCmdBlocking(HCCMD_URCPORT_ONLY, pHandle->GetDevice(), "AT+CGACT?\r", CMDOPT_NONE, APIID_GETGPRSCONTEXTACTIVATEDLIST, 0, 0, 0, ParseGetGPRSContextActivatedList, NULL, (LPVOID)(iContext-1));
	
	if (g_RILCGACT[iContext-1] == 1) {
		if( SUPPORTS_ONLY_DATAMODE && (iContext == 1) && ((bool)queryGPRSPDPContextStatus(pHandle))){
			RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: RILDrv_EnterGPRSDataMode() P2-1 skip PDP context handle action\r\n")));
		}else{
			char szCmdDeact[25];

	        (void)_snprintfz(szCmdDeact, 25, "AT+CGACT=0,%d\r",  iContext);
	        (void)QueueInternalCmd(HCCMD_STANDARD, pHandle->GetDevice(), szCmdDeact, CMDOPT_NONE, APIID_GETGPRSCONTEXTACTIVATEDLIST, 0, 0, 0, ParseGetGPRSContextActivatedList, NULL, (LPVOID)(iContext-1));
		}
    }
    g_RILCGACT[iContext-1] = uiCGACTCache;
    g_dwCurrentContextID = iContext;

    // Unfortunately the Microsoft MMI doesn't allow to adjust the authentication type (PAP, CHAP), so
    // we have to configure this with a registry key.
    if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("SetGPRSAuthentication"), &dwAuth))
    {
        char szCmdAuth[25];

        if (dwAuth >= NUM_GPRSAUTHCONFIG)
            dwAuth = 2;     // Default value is '2' (CHAP and PAP)
        (void)_snprintfz(szCmdAuth, 25, "AT^SCFG=\"GPRS/Auth\",\"%d\"\r",  g_GPRSAuthConfig[dwAuth]);

        (void)QueueInternalCmdBlocking(HCCMD_STANDARD, pHandle->GetDevice(), szCmdAuth, CMDOPT_NONE, APIID_ENTERGPRSDATAMODE, 0, 0, 0);
    }

#ifdef RIL_ALLOW_TRACE_PPP_DATA
    g_ComManager.TraceExtData(TRUE, FALSE);
#endif
    if (!QueueCmd(HCCMD_DATAPORT_ONLY,
                  pHandle,
                  szCmd,
                  dwOptions,
                  APIID_ENTERGPRSDATAMODE,
                  pfnParse,  // Parse fn
                  NULL,     // Ptr to notification data
                  hr,
                  ParseEnterGPRSDataModeError))
    {
        hr = E_FAIL;
        goto Error;
    }

    Error:
    return hr;
}
/*
+CGPADDR: <cid>,<PDP_addr>
[<CR><LF>+CGPADDR: <cid>,<PDP_addr>
[...]]
*/
static HRESULT ParseGetGPRSAddress(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    UINT nValue;
    HRESULT hr = E_FAIL;
    LPCSTR szAddr, szAddrEnd;
    LPTSTR wszAddrReturned=NULL;
    DWORD dwStringLength=0;
    pBlob = NULL;
    cbBlob = 0;

    // Note: The pdp address is optional, so ignore it if it's not there and return an empty string
    // Also, ericsson doesn't even give us the cid, so ignore that if it's not present also

    if (  (!ParseRspPrefix(szRsp, szRsp))
       || (!MatchStringBeginning(szRsp, "+CGPADDR: ", szRsp))
       || (!ParseUInt(szRsp, TRUE, nValue, szRsp))
       || (!ParseRspParamSeparator(szRsp, szRsp)) )
    {
        // Rig szAddr so that if we don't find anything, we'll return an empty string
        szAddr=szAddrEnd="\r\n";
    }
    else
    {
        szAddr = szRsp;

        // Parse ,<pd1>[,...[,pdN]]
        szAddrEnd = strstr(szAddr, "\r\n");
        if (!szAddrEnd)
        {
            goto Error;
        }

        szRsp = szAddrEnd;
    }

    // Set dwParameterLength to be size of string in bytes counting null char at end
    dwStringLength = ((szAddrEnd-szAddr)+1);
    wszAddrReturned = (LPTSTR)AllocBlob(dwStringLength * sizeof(TCHAR));
    if (!wszAddrReturned)
    {
        goto Error;
    }

    (void)wcsncpyz(wszAddrReturned, WideString(szAddr,'\r'), dwStringLength);

    pBlob = (void*)wszAddrReturned;
    cbBlob = dwStringLength * sizeof(TCHAR);
    hr = S_OK;

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

    return hr;
}

// +CGPADDR=<dwContextID>[,<dwContextID>...]
HRESULT RILDrv_GetGPRSAddress (DWORD dwParam, DWORD dwContextID)
{
    HRESULT hr = E_INVALIDARG;
    COM_PORT_TYPE        iPort;

    iPort = g_ComManager.GetPortFromContextId(dwContextID);

    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle || iPort == INVALID_PORT)
    {
        goto Error;
    }

    // Build command
    char szCmd[MAX_ATCMD_LEN];
    (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CGPADDR=%u\r", dwContextID);

    if (!QueueCmd(HCCMD_STANDARD,
                  pHandle,
                  szCmd,
                  CMDOPT_GENERATE_ERROR_CODE,
                  APIID_GETGPRSADDRESS,
                  ParseGetGPRSAddress,  // Parse fn
                  NULL,     // Ptr to notification data
                  hr))
    {
        hr = E_FAIL;
        goto Error;
    }

    Error:
    return hr;
}

// +CGANS=<params>
/*
typedef struct rilgprsanswer_tag
{
    DWORD cbSize;                       // @field structure size in bytes
    BOOL fAnswer;                       // @parm TRUE: accept, FALSE: reject
    DWORD dwL2Protocol;                 // @parm an optional RILL2PROTOCOL_* constant
    DWORD dwNumContexts;                // @parm number of contexts which follow
    DWORD dwContextID[];                // @parm identifies the context(s) to enter data state
} RILGPRSANSWER, *LPRILGPRSANSWER;
*/
HRESULT RILDrv_GPRSAnswer (DWORD dwParam, const RILGPRSANSWER *lpGprsAnswer)
{
    HRESULT hr = E_INVALIDARG;
    char szCmd[MAX_ATCMD_LEN];
    LPSTR szWalk = szCmd;
    DWORD i;
    COM_PORT_TYPE iPort;

    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        goto Error;
    }

    if (lpGprsAnswer->dwNumContexts != 1)
    {
        RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: Tried to open %u contexts at the same time.\r\n"), lpGprsAnswer->dwNumContexts));
        hr = E_INVALIDARG;
        goto Error;
    }

    iPort = g_ComManager.GetPortFromContextId(lpGprsAnswer->dwContextID[0]);
    if (iPort == INVALID_PORT)
    {
        goto Error;
    }

    // Build command

    (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CGANS=%u,", lpGprsAnswer->fAnswer ? 1 : 0);
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27

    LPCSTR szString;
    if (!StringFromFlag(lpGprsAnswer->dwL2Protocol,g_GprsL2Protocols, NUM_GPRSL2PROTOCOLS,szString))
    {
        goto Error;
    }
    (void)_snprintfz(szWalk, MAX_ATCMD_LEN, "\"%s\"", szString);
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27

    for (i=0;i<lpGprsAnswer->dwNumContexts;i++)
    {
        (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), ",%u", lpGprsAnswer->dwContextID[i]);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    }

    // Add "\r"
    (void)strncpyz(szWalk, "\r", MAX_ATCMD_LEN - (szWalk - szCmd));

    if (!QueueCmd(HCCMD_STANDARD,
                  pHandle,
                  szCmd,
                  CMDOPT_NONE,
                  APIID_GPRSANSWER,
                  NULL,  // Parse fn
                  NULL,     // Ptr to notification data
                  hr))
    {
        hr = E_FAIL;
        goto Error;
    }

    Error:
    return hr;
}

static HRESULT ParseGetPSInfoGPRSRegistrationStatus(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    UINT nValue, nDummy;
    HRESULT hr = E_FAIL;
    DWORD *pdwRegStatus = NULL;

    pBlob = NULL;
    cbBlob = 0;

    pdwRegStatus = (DWORD*)AllocBlob(sizeof(DWORD));
    if (!pdwRegStatus)
    {
        goto Error;
    }
    // Parse "<prefix>"
    // Parse "^SIND: psinfo,<urc>,"
    if (!ParseRspPrefix(szRsp, szRsp) ||
        !MatchStringBeginning(szRsp, "^SIND: psinfo,", szRsp) ||
        !ParseUInt(szRsp, TRUE, nDummy, szRsp) ||
        !MatchStringBeginning(szRsp, ",", szRsp))
    {
        goto Error;
    }

    // Parse <status>
    if (!ParseUInt(szRsp, TRUE, nValue, szRsp))
    {
        goto Error;
    }

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

    if (nValue == 0 || (nValue&1) == 1)
        *pdwRegStatus = RIL_REGSTAT_UNREGISTERED;
    else
        *pdwRegStatus = g_dwGPRSRegStatus;

    pBlob = (void*)pdwRegStatus;
    cbBlob = sizeof(DWORD);
    hr = S_OK;

Error:
    return hr;
}


static HRESULT ParseGetGPRSRegistrationStatus(LPCSTR /*szRsp*/, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    HRESULT hr = E_FAIL;
    DWORD *pdwRegStatus=NULL;
    pBlob = NULL;
    cbBlob = 0;

    pdwRegStatus = (DWORD*)AllocBlob(sizeof(DWORD));
    if (!pdwRegStatus)
    {
        goto Error;
    }

    *pdwRegStatus = g_dwGPRSRegStatus;

    pBlob = (void*)pdwRegStatus;
    cbBlob = sizeof(DWORD);
    hr = S_OK;

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

    return hr;
}

// +CGREG?
HRESULT RILDrv_GetGPRSRegistrationStatus (DWORD dwParam)
{
    HRESULT hr = E_INVALIDARG;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        goto Error;
    }

    if (!NO_CHECK_BEFORE_GPRS_REATTACH && !NO_SIND_PSINFO_IN_CDMA && (g_dwGPRSRegStatus == RIL_REGSTAT_HOME || g_dwGPRSRegStatus == RIL_REGSTAT_ROAMING))
    {
        // The module reports, that we are attached to GPRS, but the current cell might not
        // have GPRS, so we have to check with AT^SIND=psinfo if the cell really supports GPRS.
        // This doesn't apply to the EHS5 Rel1.
        if (!QueueCmd(HCCMD_URCPORT_ONLY, pHandle, "AT^SIND=psinfo,2\r", CMDOPT_NONE, APIID_GETGPRSREGISTRATIONSTATUS, ParseGetPSInfoGPRSRegistrationStatus,  NULL, hr))
        {
            hr = E_FAIL;
            goto Error;
        }
    }
    else
    {
        if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_GETGPRSREGISTRATIONSTATUS, ParseGetGPRSRegistrationStatus,  NULL, hr))
        {
            hr = E_FAIL;
            goto Error;
        }
    }

    Error:
    return hr;
}

// +CGCLASS: <class>
static HRESULT ParseGetGPRSClass(LPCSTR /*szRsp*/, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    HRESULT hr = ParseFakeDWord(RIL_GPRSCLASS_GSMORGPRS, pBlob, cbBlob);

    return hr;
}

// +CGCLASS?
HRESULT RILDrv_GetGPRSClass (DWORD dwParam)
{
    HRESULT hr = E_INVALIDARG;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);

    if (!pHandle)
    {
        goto Error;
    }

    if (!QueueCmd(HCCMD_STANDARD,
                  pHandle,
                  NULL,
                  CMDOPT_NOOP,
                  APIID_GETGPRSCLASS,
                  ParseGetGPRSClass,  // Parse fn
                  NULL,     // Ptr to notification data
                  hr))
    {
        hr = E_FAIL;
        goto Error;
    }

    Error:
    return hr;
}

// +CGCLASS=[A|B|C|CG|CC]
HRESULT RILDrv_SetGPRSClass (DWORD dwParam, DWORD dwClass)
{
    HRESULT hr = E_INVALIDARG;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);

    if (!pHandle)
    {
        goto Error;
    }

    // The HC25/HC28 doesn't allow change of class, so the RIL assumes, it is fixed to class B.
    if (dwClass != RIL_GPRSCLASS_GSMORGPRS)
        return E_INVALIDARG;

    if (!QueueCmd(HCCMD_STANDARD,
                  pHandle,
                  NULL,
                  CMDOPT_NOOP,
                  APIID_GETGPRSCLASS,
                  ParseGetGPRSClass,  // Parse fn
                  NULL,     // Ptr to notification data
                  hr))
    {
        hr = E_FAIL;
        goto Error;
    }

    Error:
    return hr;
}

/*
+CGSMS: <service>
<service>: a numeric parameter which indicates the service or service preference to be used
0 GPRS
1 circuit switched
2 GPRS preferred (use circuit switched if GPRS not available)
3 circuit switched preferred (use GPRS if circuit switched not available)
*/
static HRESULT ParseGetMOSMSService(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    UINT nValue;
    HRESULT hr = E_FAIL;
    DWORD *pdwMoSmsService=NULL;
    pBlob = NULL;
    cbBlob = 0;

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

    // Parse "+CGSMS: "
    if (!MatchStringBeginning(szRsp, "+CGSMS: ", szRsp))
    {
        goto Error;
    }

    // Parse <service>
    if (!ParseUInt(szRsp, TRUE, nValue, szRsp))
    {
        goto Error;
    }

    pdwMoSmsService = (DWORD*)AllocBlob(sizeof(DWORD));
    if (!pdwMoSmsService)
    {
        goto Error;
    }

    (void)FlagFromValue(nValue,g_GprsMOSMSService,NUM_GPRSMOSMSSERVICE,*pdwMoSmsService);

    pBlob = (void*)pdwMoSmsService;
    cbBlob = sizeof(DWORD);
    hr = S_OK;

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

    return hr;
}

static HRESULT ParseGetMOSMSServiceCdma(LPCSTR /*szRsp*/, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    DWORD *pdwRoamStatus = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    pdwRoamStatus = (DWORD *)AllocBlob(sizeof(DWORD));
    if (!pdwRoamStatus) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }

    *pdwRoamStatus = RIL_MOSMSSERVICE_CIRCUITPREFERRED;

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

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

// +CGSMS?
HRESULT RILDrv_GetMOSMSService (DWORD dwParam)
{
    HRESULT hr = E_INVALIDARG;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        goto Error;
    }

    if (pHandle->GetDevice()->GetCurrentTechnology() == TEC_MODE_CDMA) {
        if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_IGNORERADIOOFF|CMDOPT_NOOP, APIID_GETMOSMSSERVICE, ParseGetMOSMSServiceCdma, NULL, hr)) {
            hr = E_FAIL;
            goto Error;
        }
    }
    else {
        if (!QueueCmd(HCCMD_STANDARD,
                      pHandle,
                      "AT+CGSMS?\r",
                      CMDOPT_NONE,
                      APIID_GETMOSMSSERVICE,
                      ParseGetMOSMSService,  // Parse fn
                      NULL,     // Ptr to notification data
                      hr))
        {
            hr = E_FAIL;
            goto Error;
        }
    }

    Error:
    return hr;
}

// +CGSMS=[0|1|2|3]
HRESULT RILDrv_SetMOSMSService (DWORD dwParam, DWORD dwMoSmsService)
{
    UINT Value;
    HRESULT hr = E_INVALIDARG;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        goto Error;
    }

    // Build command
    char szCmd[MAX_ATCMD_LEN];
    if (!ValueFromFlag(dwMoSmsService,g_GprsMOSMSService,NUM_GPRSMOSMSSERVICE,Value))
    {
        goto Error;
    }
    (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CGSMS=%u\r",Value);


    if (!QueueCmd(HCCMD_STANDARD,
                  pHandle,
                  szCmd,
                  CMDOPT_NONE,
                  APIID_SETMOSMSSERVICE,
                  NULL,  // Parse fn
                  NULL,  // Ptr to notification data
                  hr))
    {
        hr = E_FAIL;
        goto Error;
    }

    Error:
    return hr;
}

// +CGCLASS: (list of supported <class>s)
HRESULT ParseGetGprsCapsClass(LPCSTR /*szRsp*/, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    HRESULT hr = ParseFakeDWord(RIL_GPRSCLASS_GSMORGPRS, pBlob, cbBlob);

    return hr;
}

// +CGDCONT: (range of supported <cid>s),<PDP_type>,,,(list of supported <d_comp>s),(list of supported <h_comp>s)[,(list of supported <pd1>s)[,...[,(list of supported <pdN>s)]]]
// [<CR><LF>+CGDCONT: (range of supported <cid>s),<PDP_type>,,,(list of supported <d_comp>s),(list of supported <h_comp>s)[,(list of supported <pd1>s)[,...[,(list of supported <pdN>s)]]]
// [...]]
#pragma warning( push )
#pragma warning(disable : 4815)
HRESULT ParseGetGprsCapsContext(LPCSTR /*szRsp*/, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    RILGPRSCONTEXTCAPS *pGPRSContextCaps;
    HRESULT hr = E_FAIL;

    pBlob = NULL;
    cbBlob = 0;

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

    // All QCT based modules only supports one context via PPP.
    pGPRSContextCaps->ContextIDRange.dwMinValue = 1;
    pGPRSContextCaps->ContextIDRange.dwMaxValue = 1;

    pGPRSContextCaps->dwParams|= RIL_PARAM_GCONT_CONTEXTID;

    pGPRSContextCaps->dwProtocolType = RIL_GPRSPROTOCOL_IP;
    pGPRSContextCaps->dwParams|= RIL_PARAM_GCONT_PROTOCOLTYPE;

    pGPRSContextCaps->dwDataCompression = RIL_GPRSDATACOMP_OFF;
    pGPRSContextCaps->dwParams|= RIL_PARAM_GCONT_DATACOMPRESSION;

    pGPRSContextCaps->dwHeaderCompression = RIL_GPRSHEADERCOMP_ON;
    pGPRSContextCaps->dwParams|= RIL_PARAM_GCONT_HEADERCOMPRESSION;

    // Init size of GPRSContextCaps
    pGPRSContextCaps->cbSize = sizeof(RILGPRSCONTEXTCAPS);

    pBlob = (void*)pGPRSContextCaps;
    cbBlob = sizeof(RILGPRSCONTEXTCAPS);

    hr = S_OK;

Error:
    return hr;
}
#pragma warning( pop )

// +XXXXXX: <PDP_type>, (list of supported <precedence>s), (list of supported <delay>s), (list of supported <reliability>s) , (list of supported <peak>s), (list of supported <mean>s)
// [<CR><LF>+XXXXXX: <PDP_type>, (list of supported <precedence>s), (list of supported <delay>s), (list of supported <reliability>s) , (list of supported <peak>s), (list of supported <mean>s)
// [...]]
static HRESULT ParseQOSCapsList(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPCSTR szCmd)
{
    PBYTE pBuffer=NULL;
    DWORD dwBufferSize=0;

    RILGPRSQOSPROFILECAPS *pQOSProfileCaps;

    char szValue[MAX_ATCMD_LEN];

    HRESULT hr = E_FAIL;
    BOOL bSuccess;

    pBlob = NULL;
    cbBlob = 0;

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

    // Parse "+XXXXXX: "
    while (MatchStringBeginning(szRsp, szCmd, szRsp))
    {
        // Reallocate our buffer for the additional data
        pBuffer = (PBYTE)ReallocBlob(pBuffer,dwBufferSize+sizeof(RILGPRSQOSPROFILECAPS));
        if (!pBuffer)
        {
            goto Error;
        }

        pQOSProfileCaps = (RILGPRSQOSPROFILECAPS *)(pBuffer+dwBufferSize);
        memset(pQOSProfileCaps, 0x00, sizeof(RILGPRSQOSPROFILECAPS));
        pQOSProfileCaps->cbSize=sizeof(RILGPRSQOSPROFILECAPS);

        dwBufferSize+=sizeof(RILGPRSQOSPROFILECAPS);

        // Parse <PDP_type>
        if (!ParseString(szRsp, szValue, MAX_ATCMD_LEN, szRsp))
        {
            goto Continue;
        }

        (void)FlagFromString(szValue, g_GprsPDPTypes, NUM_GPRSPDPTYPES,pQOSProfileCaps->dwProtocolType);

        // Parse ,(list of supported <precedence>s)
        if (!ParseRspParamSeparator(szRsp, szRsp) ||
           (!ParseRangeList(szRsp, g_GprsPrecedenceClass, NUM_GPRSPRECEDENCECLASS, pQOSProfileCaps->dwPrecedenceClass, szRsp)) )
        {
           goto Continue;
        }
        pQOSProfileCaps->dwParams|= RIL_PARAM_GQOSP_PRECEDENCECLASS;

        // ,(list of supported <delay>s)
        if (!ParseRspParamSeparator(szRsp, szRsp) ||
           (!ParseRangeList(szRsp, g_GprsDelayClass,NUM_GPRSDELAYCLASS, pQOSProfileCaps->dwDelayClass, szRsp)) )
        {
            goto Continue;
        }
        pQOSProfileCaps->dwParams|= RIL_PARAM_GQOSP_DELAYCLASS;

        // Parse ,(list of supported <reliability>s)
        if (!ParseRspParamSeparator(szRsp, szRsp) ||
           (!ParseRangeList(szRsp, g_GprsReliabilityClass,NUM_GPRSRELIABILITYCLASS, pQOSProfileCaps->dwReliabilityClass, szRsp)) )
        {
            goto Continue;
        }
        pQOSProfileCaps->dwParams|= RIL_PARAM_GQOSP_RELIABILITYCLASS;

        // Parse ,(list of supported <peak>s)
        if (!ParseRspParamSeparator(szRsp, szRsp) ||
           (!ParseRangeList(szRsp, g_GprsPeakThruClass,NUM_GPRSPEAKTHRUCLASS, pQOSProfileCaps->dwPeakThruClass, szRsp)) )
        {
            goto Continue;
        }

        // Parse ,(list of supported <mean>s)
        if (!ParseRspParamSeparator(szRsp, szRsp) ||
           (!ParseRangeList(szRsp, g_GprsMeanThruClass,NUM_GPRSMEANTHRUCLASS, pQOSProfileCaps->dwMeanThruClass, szRsp)) )
        {
            goto Continue;
        }
        pQOSProfileCaps->dwParams|= RIL_PARAM_GQOSP_MEANTHRUCLASS;

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

    pBlob = (void*)pBuffer;
    cbBlob = dwBufferSize;
    hr = S_OK;

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

// +CGQREQ: <PDP_type>, (list of supported <precedence>s), (list of supported <delay>s), (list of supported <reliability>s) , (list of supported <peak>s), (list of supported <mean>s)
// [<CR><LF>+CGQREQ: <PDP_type>, (list of supported <precedence>s), (list of supported <delay>s), (list of supported <reliability>s) , (list of supported <peak>s), (list of supported <mean>s)
// [...]]
HRESULT ParseGetGprsCapsQOS(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    return ParseQOSCapsList(szRsp,pBlob,cbBlob,"+CGQREQ: ");
}

// +CGQMIN: <PDP_type>, (list of supported <precedence>s), (list of supported <delay>s), (list of supported <reliability>s) , (list of supported <peak>s), (list of supported <mean>s)
// [<CR><LF>+CGQMIN: <PDP_type>, (list of supported <precedence>s), (list of supported <delay>s), (list of supported <reliability>s) , (list of supported <peak>s), (list of supported <mean>s)
// [...]]
HRESULT ParseGetGprsCapsQOSMin(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    return ParseQOSCapsList(szRsp,pBlob,cbBlob,"+CGQMIN: ");
}

// +CGSMS: (list of currently available <service>s)
HRESULT ParseGetGprsCapsSMOSMS(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    HRESULT hr = E_FAIL;
    DWORD *pdwMoSmsService=NULL;
    pBlob = NULL;
    cbBlob = 0;

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

    // Parse "+CGDCONT: "
    if (!MatchStringBeginning(szRsp, "+CGSMS: ", szRsp))
    {
        goto Error;
    }

    pdwMoSmsService = (DWORD*)AllocBlob(sizeof(DWORD));
    if (!pdwMoSmsService)
    {
        goto Error;
    }

    // Parse (list of currently available <service>s)
    if (!ParseRangeList(szRsp, g_GprsMOSMSService,NUM_GPRSMOSMSSERVICE, *pdwMoSmsService, szRsp))
    {
        goto Error;
    }

    pBlob = (void*)pdwMoSmsService;
    cbBlob = sizeof(DWORD);
    hr = S_OK;

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

    return hr;
}

// CheckDefaultAPN()
// Relevant for Data-Only-LTE: the module won't register to the LTE network unless
// an APN is defined. To make sure, that an APN is always set the RIL can set a
// default APN for the current SIM card or network.
void CRilHandle::CheckDefaultAPN()
{
    char szIMSI[MAX_ATCMD_LEN];

    memset(szIMSI, 0, MAX_ATCMD_LEN);

    // Query IMSI
    if (QueueInternalCmdBlocking(HCCMD_STANDARD, this, "AT+CIMI\r", CMDOPT_SUPPRESSLOGGING, APIID_GETUSERIDENTITY, 0, 0, 0, ParseGetUserIdentity, NULL, (LPVOID)szIMSI)) {
        szIMSI[MAX_ATCMD_LEN-1] = '\0';
        if (strlen(szIMSI) > 0) {
            WCHAR wszAPN[MAX_ATCMD_LEN];

            memset(wszAPN, 0, sizeof(wszAPN));

            // 1) Search for entry for this specific SIM card.
            (void)GetRegistrySZ(HKEY_LOCAL_MACHINE, TEXT("Software\\Microsoft\\RIL\\DefaultAPN"), TString(szIMSI), wszAPN, MAX_ATCMD_LEN);

            // 2) If not found try an entry for the network operator who issued the SIM
            if (wcslen(wszAPN) == 0) {
                // Check for 5-digit or 6-digit MCC/MNC
                if (strncmp(szIMSI, "310", 3) == 0 || strncmp(szIMSI, "311", 3) == 0 || strncmp(szIMSI, "302", 3) == 0 || strncmp(szIMSI, "405", 3) == 0)
                    szIMSI[6] = '\0';
                else
                    szIMSI[5] = '\0';

                // Read registry key.
                (void)GetRegistrySZ(HKEY_LOCAL_MACHINE, TEXT("Software\\Microsoft\\RIL\\DefaultAPN"), TString(szIMSI), wszAPN, MAX_ATCMD_LEN);
            }

            // If default APN is defined, apply it.
            if (wcslen(wszAPN) > 0) {
                (void)_snprintfz(szIMSI, MAX_ATCMD_LEN, "AT+CGDCONT=1,\"IP\",\"%S\"\r", wszAPN);
                (void)QueueInternalCmd(HCCMD_STANDARD, this, szIMSI, CMDOPT_NONE, APIID_SETGPRSCONTEXT, 0, 0, 0);
            }
        }
    }
}

