//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES.
//

#include <enfora.h>


CRITICAL_SECTION g_csGPRSDeactLock;
bool g_fDeactNotificationRcvd = FALSE;
bool g_fDeactResponseRcvd = FALSE;
DWORD g_LastDeactNotificationRcvd = 0;

// Phonebook storage locations  - NEEDS to be SORTED

static const LISTSTRINGMAP g_rglsmCapsPBLocations[] =
{
    { "EN",   RIL_CAPS_PBLOC_SIMEMERGENCY   },
    { "FD",   RIL_CAPS_PBLOC_SIMFIXDIALING  },
    { "LD",   RIL_CAPS_PBLOC_SIMLASTDIALING },
    { "MT",   RIL_CAPS_PBLOC_SIMPHONEBOOK   },           // RIL_PBLOC_SIMPHONEBOOK (MT is used for TI phonebook)
    { "ON",   RIL_CAPS_PBLOC_OWNNUMBERS     },
};

// Phonebook storage locations - does NOT NEED to be SORTED

static const LPCSTR g_rgszPBLocations[] =
{
    "",             // RIL_PBLOC_UNKNOWN
    "EN",           // RIL_PBLOC_SIMEMERGENCY
    "FD",           // RIL_PBLOC_SIMFIXDIALING
    "LD",           // RIL_PBLOC_SIMLASTDIALING
    "ON",           // RIL_PBLOC_OWNNUMBERS
    "MT",           // RIL_PBLOC_SIMPHONEBOOK (MT is used for TI phonebook)
};

#ifdef SUPPRESS_ONLINE_BATTERY_INFO
char gszGetBatteryInfoLast[128];
#endif // SUPPRESS_ONLINE_BATTERY_INFO

// OEM1-specific error results must be in left hand sorted order
static const ERRORMAP g_rgemCMEErrors[] =
{
    { RIL_E_TIMEDOUT,          512},
};

// OEM1-specific error results must be in left hand sorted order
static const ERRORMAP g_rgemEXTErrors[] =
{
    { RIL_E_OPNOTALLOWED,        0},
    { RIL_E_OPNOTALLOWED,        1},
    { RIL_E_OPNOTALLOWED,        2},
    { RIL_E_OPNOTALLOWED,        3},
    { RIL_E_OPNOTALLOWED,        4},
    { RIL_E_INVALIDDIALSTRING,   5},
    { RIL_E_INVALIDDIALSTRING,   6},
    { RIL_E_INVALIDDIALSTRING,   7},
    { RIL_E_INVALIDDIALSTRING,   8},
    { RIL_E_INVALIDDIALSTRING,   9},
    { RIL_E_INVALIDDIALSTRING,  10},
    { RIL_E_INVALIDDIALSTRING,  11},
    { RIL_E_OPNOTALLOWED,       12},
    { RIL_E_OPNOTALLOWED,       13},
    { RIL_E_OPNOTALLOWED,       14},
    { RIL_E_OPNOTALLOWED,       -1},    // unsigned value is highest for binary search
};

// %CPI is TI and Enfora specific: define TI and Enfora specific notification codes here
#define TI_CPISTAT_SETUP    0
#define TI_CPISTAT_DISCONNECTED    1
#define TI_CPISTAT_ALERT    2
#define TI_CPISTAT_CALLPROCEED    3
#define TI_CPISTAT_SYNCH    4
#define TI_CPISTAT_PROGRESS    5
#define TI_CPISTAT_CONNECTED    6
#define TI_CPISTAT_MOSETUP    7
#define TI_CPISTAT_RELEASE    9
#define TI_CPISTAT_REJECT    10

// above are the only ones we care about, the others are
// 0 = setup message
// 2 = alert message
// 3 = call proceeding message
// 5 = progress message

#define LAST_CPI_ID 11
// RIL registry key
const LPCTSTR g_tszRegKeyRIL = TEXT("Drivers\\BuiltIn\\RIL");

// HW-SPECIFIC: The following init commands are hardware-specific
// NOTES: Z                 resets the AT interface
//        +CPIN?            initializes the SIM (OEM1 only)
//        S0=0              disables auto-answer
//        E0                disables command echo
//        Q0                enable result codes
//        V0                enable non-verbose responses
//        X3                enable busy signal detection and additional info in CONNECT response (OEM1 only)
//        X4                enable busy signal detection and additional info in CONNECT response
//        &C1               enables DCD to be used for carrier detection
//        &D1               enables DTR to be used for dropping out of data mode
//        +CMEE=1           enables +CME ERROR and +CMS ERROR responses
//        +CRC=1            enables +CRING notifications
//        +CR=1             enables +CR notifications
//        +CMOD=0           enables single call mode
//        +CREG=1           enables short +CREG notifications
//        +CREG=2           enables extended +CREG notifications (w/ <lac> and <ci>)
//        +FCLASS=0         enables data mode
//        +CSSN=0,1         enables +CSSU notifications
//        +CHSR=1           enables +CHSR notifications
//        +COLP=1           enables +COLP notifications
//        +CCWA=1           enables +CCWA notifications
//        %SATC=1           enables %SATI, %SATN, and %SATA notifications (OEM1 only)
//        +CMGF=0           enables SMS PDU mode
//        +CNMI=2,2,2,1,0   enables +CMT, +CBM, and +CDS notifications
//        +CSCS="8859-1"    sets default alphabet to ISO 8859-1
//        %CSQ=1            enables signal quality notifications (OEM1)

// supports init levels below:
//    COM_INIT_INDEX - index 0
//    COM_REINIT_INDEX - index 1
//    COM_SMSINIT_INDEX - index 2

const INITSTRING_DATA g_rgisdModuleInit[] =
{
    // OEM1 init commands
    { "V0|E0|S0=0|Q0|X3|&C1|&D1|%DATA=2,\"UART\",0,2,\"SER\",\"UART\",0,1|+CMEE=1;+CRC=1;+CR=1;+CMOD=0;+CMUT=0|$VSELECT=1|%CGAATT=0,1|%CTZV=1|"
#ifndef RIL_NO_CREG_LOCATION_UPDATES
    "+CREG=2;"
#else
    "+CREG=1;"
#endif // RIL_NO_CREG_LOCATION_UPDATES
    "+CSSN=0,1;+COLP=0;+CCWA=1;+CSCS=\"HEX\";+CMGF=0|+CGREG=1|+CUSD=1|"
    "%SATC=1|"
    "%CSQ=1|%CPI=3|+CGEREP=1,0|%CSTAT=1|"
#ifdef RIL_CELL_BROADCAST
    "+CNMI=2,2,2,1,0|",
#else
    "+CNMI=2,2,0,1,0|",
#endif
    CMDOPT_INIT | CMDOPT_GENERALINIT},                                             // OEM1 Init index
     {"E0|+CMUT=0|", CMDOPT_INIT | CMDOPT_GENERALINIT},
#ifdef RIL_CELL_BROADCAST
     // OEM1 Re-init index
    { "+CNMI=2,2,2,1,0|", CMDOPT_INIT | CMDOPT_SMSINIT},                         // OEM1 SMS-init index
#else
     { "+CNMI=2,2,0,1,0|", CMDOPT_INIT | CMDOPT_SMSINIT},                         // OEM1 SMS-init index
#endif
};

// This driver doesn't have a way to query current
// uplink and downlink volume, so we have to cache them.
DWORD   g_dwCacheUplinkVolume;
DWORD   g_dwCacheDownlinkVolume;

// Forward declarations

static HRESULT ParseGetCurrentAddressId(LPCSTR szRsp, void*& pBlob, UINT& cbBlob);
static HRESULT ParseATRInfo(LPCSTR szRsp, void*& pBlob, UINT& cbBlob);
static HRESULT ParseGetSimToolkitProfile(LPCSTR szRsp, void*& pBlob, UINT& cbBlob);
static HRESULT ParseSendSimToolkitEnvelopeCmd(LPCSTR szRsp, void*& pBlob, UINT& cbBlob);
static HRESULT ParseBuildInfo(LPCSTR szRsp, void*& pBlob, UINT& cbBlob);
static HRESULT ParseBatteryInfo(LPCSTR szRsp, void*& pBlob, UINT& cbBlob);
static HRESULT ParseDAR(LPCSTR szRsp, void*& pBlob, UINT& cbBlob);
static HRESULT ParseGetNITZSupported( __in LPCSTR szRsp, __out void*& pBlob, __out UINT& cbBlob );

// PDD parameter table

const RILPDDParams rppTIPDDParams = {
    sizeof(rppTIPDDParams),                                 // cbSize
    true,                                                   // fRadioSignonRequired
    true,                                                   // fIgnoreUnsolicitedNoCarrier
    ParseGetCurrentAddressId,                               // pfnParseGetCurrentAddressId
    ParseATRInfo,                                           // pfnParseATRInfo
    g_rglsmCapsPBLocations,                                 // plsmPhoneBookLocationCapsTable
    sizeof(g_rglsmCapsPBLocations) / sizeof(LISTSTRINGMAP), // uiPhoneBookLocationCapsTableSize
    RIL_SIMTKN_RADIOIMPLEMENTS_REQUESTMEINPUT,              // dwDevCapsSIMTKN_SetUpCall
    RIL_SIMTKN_RADIOIMPLEMENTS_NOTIFICATION,                // dwDevCapsSIMTKN_SendDTMF
    false,                                                  // fNotificationPortSupported
    RIL_SIMCMD_GETRESPONSE,                                 // dwCRSMStatusCommandId
    15,                                                     // RestrictedSimAccessSetCmdParam3 (3GPP TS 27.007 V6.8.0 (2005-03) sec. 8.18 Restricted SIM access +CRSM)
    true,                                                   // fGPRSQoSEmptyParameterListAllowed
    g_rgszPBLocations,                                      // pstrPhoneBookLocationTable
    sizeof(g_rgszPBLocations) / sizeof(LPCSTR),             // uiPhoneBookLocationTableSize
    false,                                                  // fIsRadioOffInMinimumPowerState
    false,                                                  // fDontTurnRadioOnIfAlreadyOn
    true,                                                   // fSendSimCmdSupported
    false,                                                  // fCRSMResponseQuoted
    ParseGetSimToolkitProfile,                              // pfnParseGetSimToolkitProfile
    ParseSendSimToolkitEnvelopeCmd,                         // pfnParseSendSimToolkitEnvelopeCmd
    false,                                                  // fPowerManagementSupported
    ParseBuildInfo,                                         // pfnParseBuildInfo
    ParseBatteryInfo,                                       // pfnParseBatteryInfo
    ENCODING_GSMDEFAULT_HEX,                                // etMessageEncodingType
    ParseDAR,                                               // pfnParsePostRebootDiagnostics
    0,                                                      // dwPostRebootInitDelay
    g_rgemCMEErrors,                                        // pemCMEErrorTable
    sizeof(g_rgemCMEErrors) / sizeof(ERRORMAP),             // uiCMEErrorTableSize
    NULL,                                                   // pemCMSErrorTable
    0,                                                      // uiCMSErrorTableSize
    g_rgemEXTErrors,                                        // pemEXTErrorTable
    sizeof(g_rgemEXTErrors) / sizeof(ERRORMAP),             // uiEXTErrorTableSize
    "> \r",                                                 // pchSMSIntermediaryPrompt
    true,                                                   // fIgnoreDeniedNotificationsUntilUnlock
    false,                                                  // fRequireWaitingForSMSReady
    false,                                                  // fRequireWaitingForSIMPhoneBookReady
    true,                                                   // fExplicitRadioReadyNotification
    false,                                                  // fSIMToolkit_SupportsTextNotifications
    false,                                                  // fSIMToolkit_SupportsTextFormat
    false,                                                  // fByteArrayIsQuoted
    true,                                                   // fSetAudioDevicesSupported
    DEFAULT_COM_MASK,                                       // dwDefaultCOMMask
    ENCODING_TECHARSET,                                     // etEncodingTECharset
    false,                                                  // fSendRegStatusChangedAfterEveryReadyStateNotification
    true,                                                   // fSignalQualityActualLevelSupported
    g_rgisdModuleInit,                                      // pisdInitStringDataTable
    sizeof(g_rgisdModuleInit) / sizeof(INITSTRING_DATA),    // uiInitStringDataTableSize
    ParseGetNITZSupported,                                  // pfnParseGetNITZSupported
};

HRESULT PDD_IsSIMPINNotification(__in const void* rpBlob, __in const UINT rcbBlob, __in const DWORD dwCode, __out bool& fIsSIMPIN)
{
    //OEM1 does not support notification port, so no possibility of a notification on the response port
    return E_NOTIMPL;
}

HRESULT PDD_QueueResponse(__in CCommand* pCurrCommand, __inout CResponse* pRsp, __out bool& fQueueResponse)
{
    fQueueResponse = TRUE;

    // The current command is a GPRS deactivation command
    if (pCurrCommand && pCurrCommand->CmdOptIsSet(CMDOPT_DEACT)) {
    // This is a ME DEACT notification (note that only a ME DEACT notification maps to a solicited
    // response with a dwCode of RIL_NOTIFY_GPRSCONNECTIONSTATUS)
    EnterCriticalSection(&g_csGPRSDeactLock); 
    if (RIL_NOTIFY_GPRSCONNECTIONSTATUS == pRsp->GetNotifyCode()) {
        // The command response has already been received, convert the dwCode to look like a result
        // and make this as the command response, reset all the flags as we are done
        if (g_fDeactResponseRcvd) {
            // AT+CGACT, 0, ME case - COMPLETE HERE
            pRsp->MakeOK();
            g_fDeactNotificationRcvd = g_fDeactResponseRcvd = FALSE;
            }
        // Otherwise, do not queue this response as we have to wait for the actual command response
        else {
            // AT+CGACT, ME, 0 case - INCOMPLETE HERE
            fQueueResponse = FALSE;
            (VOID) pRsp->UpdateDataPointer((UINT)strlen("4\r"));
            }
        }
    // This is the response for the deactivation command
    else {
        g_fDeactResponseRcvd = TRUE;
        // The ME DEACT notification has already been received
        if (g_fDeactNotificationRcvd) {
            // The ME DEACT notification is stale, wait for a new one, do not queue this response
            // Note the calculation below is rollover safe but it will not work if the MEDEACT is as stale
            // as 49 days - the assumption is we will not have such a stale ME DEACT under normal
            // usage
            if ((GetTickCount() - g_LastDeactNotificationRcvd) > STALE_MEDEACT_THRESHOLD) {
                // Stale ME, AT+CGACT, 0 case (becomes AT+CGACT, 0, ME case) - INCOMPLETE HERE
                g_fDeactNotificationRcvd = FALSE;
                fQueueResponse = FALSE;
                }
            // The ME DEACT notification is not stale, this response becomes the real response
            else {
                // Fresh ME, AT+CGACT, 0 case - COMPLETE HERE
                g_fDeactNotificationRcvd = g_fDeactResponseRcvd = FALSE;
                }
            }
        // Otherwise, wait for a ME DEACT, do not queue this response
        else {
            // AT+CGACT, 0, ME case - INCOMPLETE HERE
            fQueueResponse = FALSE;
            (VOID) pRsp->UpdateDataPointer((UINT)strlen("0\r"));
            }
        }
    LeaveCriticalSection(&g_csGPRSDeactLock);
       }

    return S_OK;
}

HRESULT PDD_CreateCommand_SendDTMF(__out_ecount(cchCmd) LPSTR szCmd, __in const size_t cchCmd, __in LPCSTR lpszChars, __out DWORD& dwDuration, __out UINT& uiNumVals)
{
    HRESULT hr = StringCchCopyA( szCmd, cchCmd, "AT" );
    if ( FAILED( hr ) )
    {
       goto Error;
    }

    // Because we're not setting the duration, the radio is using the default
    // duration.  Make sure we use the default value so the timeout calculation
    // below is correct.
    dwDuration = 300;

    LPSTR szWalk = szCmd;
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    DEBUGCHK(NULL != szWalk);

    for (LPCSTR pchChars = lpszChars; *pchChars; pchChars++) {
        uiNumVals++;
        hr = StringCchPrintfA( szWalk, cchCmd - (szWalk - szCmd), "+VTS=%c;", *pchChars );
        if ( FAILED( hr ) )
        {
           goto Error;
        }
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        DEBUGCHK(NULL != szWalk);
    }
    // remove the trailing ";"
    *(--szWalk) = '\0';

    hr = StringCchCopyA( szWalk, cchCmd - (szWalk - szCmd), "\r" );
    if ( FAILED( hr ) )
    {
       goto Error;
    }

Error:
    return hr;
}

HRESULT PDD_CreateCommand_StartDTMF(__out_ecount(cchCmd) LPSTR szCmd, __in const size_t cchCmd, __in const char ch)
{
   return StringCchPrintfA( szCmd, cchCmd, "AT%%VTS=%c,1\r", ch );
}

HRESULT PDD_CreateCommand_StopDTMF(__out_ecount(cchCmd) LPSTR szCmd, __in const size_t cchCmd, __in const char ch)
{
   return StringCchPrintfA( szCmd, cchCmd, "AT%%VTS=%c,0\r", ch );
}
    
HRESULT PDD_IndicateCallActivityToAudioSubsystem(__in const bool fCallIsActive)
{
    //Not supported
    return E_NOTIMPL;
}

HRESULT PDD_CreateCommand_ReleaseHeld(__out_ecount(cchCmd) LPSTR szCmd, __in const size_t cchCmd)
{
   return StringCchCopyA( szCmd, cchCmd, "AT+CHLD=0\r" );
}

HRESULT PDD_CreateCommand_ReleaseCall(__out_ecount(cchCmd) LPSTR szCmd, __in const size_t cchCmd, __in const DWORD dwID)
{
   return StringCchPrintfA( szCmd, cchCmd, "AT%%CHLD=7%u\r", dwID );
}

HRESULT PDD_CreateCommand_HoldActiveAcceptHeld(__out_ecount(cchCmd) LPSTR szCmd, __in const size_t cchCmd)
{
   return StringCchCopyA( szCmd, cchCmd, "AT+CHLD=2\r" );
}

HRESULT PDD_CreateCommand_SetCurrentAddressId(__out_ecount(cchCmd) LPSTR szCmd, __in const size_t cchCmd, __in const DWORD dwAddressId)
{
   return StringCchPrintfA( szCmd, cchCmd, "AT%%ALS=%u\r", dwAddressId );   
}

HRESULT PDD_CreateCommand_GetCurrentAddressId(__out_ecount(cchCmd) LPSTR szCmd, __in const size_t cchCmd)
{
   return StringCchCopyA( szCmd, cchCmd, "AT%ALS?\r" );
}

static HRESULT ParseGetCurrentAddressId(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetCurrentAddressId);
    UINT nValue;
    DWORD* pdwAddressId = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

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

    // Parse "<prefix>%ALS: <addressID><postfix>"
    if (!ParseRspPrefix(szRsp, szRsp)                   ||
        !MatchStringBeginning(szRsp, "%ALS: ", szRsp)   ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp)          ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    *pdwAddressId = nValue;

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

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

HRESULT PDD_BeginRadioPowerOn()
{
    return E_NOTIMPL;
}

HRESULT PDD_BeginGPRSContextDeactivation()
{
    EnterCriticalSection(&g_csGPRSDeactLock); 
    // In case the previous deactivation attempt did not result in a expected ME DEACT notification
    // after a response was received, we want to be sure to not confuse the response for that
    // transaction with that for this new transaction, so we clear it
    g_fDeactResponseRcvd = FALSE;
    LeaveCriticalSection(&g_csGPRSDeactLock);

    return S_OK;
}

HRESULT PDD_WaitForSIMReady()
{
   return E_NOTIMPL;
}

HRESULT PDD_CreateCommand_GetEquipmentInfo(__out_ecount(cchCmd) LPSTR szCmd, __in const size_t cchCmd)
{
   return StringCchCopyA( szCmd, cchCmd, "AT+CGMI;+CGMM;+CGMR;D*#06#\r" );
}

HRESULT PDD_CreateCommand_SetupBeforeRadioOn(__out_ecount(cchCmd) LPSTR szCmd, __in const size_t cchCmd, __in const bool fPrefixPresent)
{
    return StringCchCopyA( szCmd, cchCmd, ( fPrefixPresent ) ? "%SATC=1,0901FF7F00000010;" : "AT%SATC=1,0901FF7F00000010;" );
}

HRESULT PDD_CreateCommand_CleanupBeforeRadioOff(__out_ecount(cchCmd) LPSTR szCmd, __in const size_t cchCmd, __in const bool fPrefixPresent)
{
    return E_NOTIMPL;
}

HRESULT PDD_CreateCommand_GetSimToolkitProfile(__out_ecount(cchCmd) LPSTR szCmd, __in const size_t cchCmd)
{
   return StringCchCopyA( szCmd, cchCmd, "AT%SATC?\r" );
}

static HRESULT ParseGetSimToolkitProfile(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetSimToolkitProfile);
    UINT i;
    UINT nValue;
    UINT cbProfileString;
    LPSTR szProfileString = NULL;
    LPCSTR pchProfileStringWalk;
    UINT cbProfile;
    BYTE* pbProfileWalk;
    BYTE* pbProfile = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

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

    // Parse "<profile>"
    // NOTE: we take ownership of allocated szProfileString
    if (!ParseUnlimitedUnquotedString(szRsp, '\r', szProfileString, cbProfileString, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    DEBUGCHK(0 == (cbProfileString - 1) % 2);
    cbProfile = (cbProfileString - 1) / 2;

    // Allocate the byte array of needed size
    pbProfile = (BYTE*)AllocBlob(cbProfile);
    if (!pbProfile) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }

    // Decode the profile
    pbProfileWalk = pbProfile;
    pchProfileStringWalk = szProfileString;
    for (i = 0; i < cbProfile; i++) {
        *pbProfileWalk = SemiByteCharsToByte(*pchProfileStringWalk, *(pchProfileStringWalk + 1));
        pbProfileWalk++;
        pchProfileStringWalk += 2;
    }

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

    pBlob = (void*)pbProfile;
    cbBlob = cbProfile;

Error:
    if (FAILED(hr)) {
        FreeBlob(pbProfile);
    }
    delete[] szProfileString;
    return hr;
}


HRESULT PDD_CreateCommand_SetSimToolkitProfile(__out_ecount(cchCmd) LPSTR szCmd, size_t cchCmd, __in_ecount(cchSTKPS) LPCSTR szSTKProfileString, size_t cchSTKPS)
{
   return StringCchPrintfA( szCmd, cchCmd, "AT%%SATC=1,%s;+CFUN=1\r", szSTKProfileString );
}

HRESULT PDD_CreateCommand_SimToolkitEnvelopeCmd(__out_ecount(cchCmd) LPSTR szCmd, __in const size_t cchCmd, __in_ecount(dwSize) const BYTE* lpbCommand, __in const DWORD dwSize)
{
    LPSTR szCmdTemp = NULL;
    HRESULT hr = E_FAIL;
    
    // Add "AT%SATE=<command><CR>"
    // NOTE: caller will take ownership of allocated szCmdTemp
    BOOL fOk = ComposeCmdWithByteArray("AT%SATE=", lpbCommand, dwSize, "\r", szCmdTemp); 

    if (fOk && szCmdTemp)
    {
        hr = StringCchCopyA( szCmd, cchCmd, szCmdTemp );
        delete [] szCmdTemp;
    }

    return hr;
}

static HRESULT ParseSendSimToolkitEnvelopeCmd(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseSendSimToolkitEnvelopeCmd);
    UINT i;
    UINT cbResponseString;
    LPSTR szResponseString = NULL;
    LPCSTR pchResponseStringWalk;
    UINT cbResponse;
    BYTE* pbResponseWalk;
    BYTE* pbResponse = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

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

    // Parse "<response>"
    // NOTE: we take ownership of allocated szResponseString
    if (!ParseUnlimitedUnquotedString(szRsp, '\r', szResponseString, cbResponseString, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    DEBUGCHK(0 == (cbResponseString - 1) % 2);
    cbResponse = (cbResponseString - 1) / 2;

    // Allocate the byte array of needed size
    pbResponse = (BYTE*)AllocBlob(cbResponse);
    if (!pbResponse) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }

    // Decode the response
    pbResponseWalk = pbResponse;
    pchResponseStringWalk = szResponseString;
    for (i = 0; i < cbResponse; i++) {
        *pbResponseWalk = SemiByteCharsToByte(*pchResponseStringWalk, *(pchResponseStringWalk + 1));
        pbResponseWalk++;
        pchResponseStringWalk += 2;
    }

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

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

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

HRESULT PDD_CreateCommand_SIMTKCmdResponsePrefix(__out_ecount(cchCmd) LPSTR szCmd, __in const size_t cchCmd, __in const DWORD dwResponseType)
{
    // For newer OEM1 stacks the SIM toolkit envelope and response command can have quotes
    // (but are not required) around the data portion of the response. Although, having
    // quotes around the data is a more proper form, we do not add them at this time to 
    // remain as compatible as possible with older stacks. 

    return StringCchCopyA( szCmd, cchCmd, ( SIM_NOTIFY_SETUPMENU_REPLY == dwResponseType ) ? "AT%SATE=" : "AT%SATR=" ); 
}

HRESULT PDD_CreateCommand_SIMTKEventDownloadPrefix(__out_ecount(cchCmd) LPSTR szCmd, __in const size_t cchCmd)
{
    return StringCchCopyA( szCmd, cchCmd, "AT%SATE=" ); 
}

HRESULT PDD_CreateCommand_BuildInfoQuery(__out_ecount(cchCmd) LPSTR szCmd, __in const size_t cchCmd)
{
   return StringCchCopyA( szCmd, cchCmd, "AT$V0\r" );
}

static HRESULT ParseBuildInfo(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    // HW-SPECIFIC: This parses response to a AT$V0 to get the firmware build number
    
    FUNCTION_TRACE(ParseBuildInfo);

    HRESULT hr = S_OK;
    CHAR * szBuildInfo = NULL;

    pBlob = NULL;
    cbBlob = 0;

    szBuildInfo = (CHAR*)AllocBlob(MAX_PATH);

    if (!ParseUnquotedString(szRsp, '\r', szBuildInfo, MAX_PATH, szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }

    pBlob = szBuildInfo;
    cbBlob = MAX_PATH;

Error:
    if (FAILED(hr)) {
        FreeBlob(szBuildInfo);
    }
    return hr;
    return S_OK;
}

HRESULT PDD_CreateCommand_BatteryInfoQuery(__out_ecount(cchCmd) LPSTR szCmd, __in const size_t cchCmd)
{
   return StringCchCopyA( szCmd, cchCmd, "AT%A0\r" );     
}

static HRESULT ParseBatteryInfo(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
#define VOLTAGE_RESP_FIELD  "ADC 1 ="   

    LPCSTR pch;
    DWORD*  pdwVoltage;

    FUNCTION_TRACE(ParseBatteryInfo);

#ifdef SUPPRESS_ONLINE_BATTERY_INFO
    LPCSTR szOrigRsp=szRsp;
#endif // SUPPRESS_ONLINE_BATTERY_INFO

    pBlob = NULL;
    cbBlob = 0;

    pdwVoltage = (DWORD *)AllocBlob(sizeof(DWORD));
    if( NULL == pdwVoltage)
    {
        ASSERT( FALSE );
        goto Error;
    }
    *pdwVoltage = 0UL;

    // search for ADC 1
    pch = const_cast<char*>(strstr( szRsp, VOLTAGE_RESP_FIELD ));
    if( NULL == pch )
    {
        ASSERT( FALSE );
        goto Error;
    }

    pch += strlen( VOLTAGE_RESP_FIELD );

    while( *pch == ' ' ) // remove leading spaces
    {
        pch++;
    }

    if ( sscanf( pch, "%x", pdwVoltage ) < 0 )
        {
        ASSERT( FALSE );
        goto Error;
        }

    pBlob = pdwVoltage;
    cbBlob = sizeof(DWORD);
    
#ifdef SUPPRESS_ONLINE_BATTERY_INFO
    strncpyz(gszGetBatteryInfoLast,szOrigRsp,sizeof(gszGetBatteryInfoLast));
#endif // SUPPRESS_ONLINE_BATTERY_INFO

    return S_OK;

Error:
    FreeBlob(pdwVoltage);
    return E_FAIL;
}

HRESULT PDD_RebootRadio()
{
    return E_NOTIMPL;
}

HRESULT PDD_CreateCommand_GetPostRebootDiagnostics(__out_ecount(cchCmd) LPSTR szCmd, __in const size_t cchCmd)
{
   // HW specific OEM1 uses %DAR to query the cause of the last radio crash.  We want this in the radio log or watson report
   return StringCchCopyA( szCmd, cchCmd, "AT%DAR\r" );
}

static HRESULT ParseDAR(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
#if RIL_WATSON_REPORT    
    g_fWatsonCalled = FALSE;
    SignalCriticalError(RILLOG_EVENT_RADIOFAILUREREASON, __LINE__, __FILE__);        
#endif // RIL_WATSON_REPORT
    return S_OK;
}


HRESULT
PDD_CreateCommand_GetNITZSupported(
    __out_ecount(cchCmd) LPSTR szCmd,
    __in size_t cchCmd
)
{
    return StringCchCopyA( szCmd, cchCmd, "AT%CTZV?\r" );
}


HRESULT PDD_GetResponseObject(__out CResponse*& pNewRsp, __in const CResponse* pSrcRsp)
{
   if (NULL == pSrcRsp) {
    pNewRsp = new CResponseTI;
    }
   else {
    pNewRsp = new CResponseTI(*(static_cast<const CResponseTI*>(pSrcRsp)));    
    }

   return (pNewRsp ? S_OK : E_OUTOFMEMORY);
}

HRESULT PDD_Initialize(__out const RILPDDParams*& prppRILPDDParams)
{
    InitializeCriticalSection(&g_csGPRSDeactLock);
    prppRILPDDParams = &rppTIPDDParams;
    return S_OK;
}

HRESULT PDD_Deinitialize()
{        
    DeleteCriticalSection(&g_csGPRSDeactLock);    
    return S_OK;
}

HRESULT PDD_CreateCommand_ChangeLockingPassword(__out_ecount(cchCmd) LPSTR szCmd, __in const size_t cchCmd, __in const DWORD dwFacility, __in LPCSTR lpszOldPassword, __in LPCSTR lpszNewPassword)
{
    // This hardware uses ATD to get the desired functionality
    return StringCchPrintfA( szCmd, cchCmd, "ATD**05%s*%s*%s*%s#\r", ((RIL_LOCKFACILITY_SIM_PIN2 == dwFacility) ? "2" : ""), lpszOldPassword, lpszNewPassword, lpszNewPassword );
}

HRESULT PDD_SetDownlinkVolume(__in const DWORD dwDownlinkVolume)
{
   g_dwCacheDownlinkVolume = dwDownlinkVolume;
   return S_OK;
}

HRESULT PDD_GetDownlinkVolume(__in const bool fNormalized, __out UINT& uiDownlinkVolume)
{
    if (fNormalized)
    {
        uiDownlinkVolume = g_dwCacheDownlinkVolume / DOWNLINK_V0LUME_NORMALIZE;
    }
    else
    {
        uiDownlinkVolume = g_dwCacheDownlinkVolume;
    }

    return S_OK;
}

HRESULT PDD_SetUplinkVolume(__in const DWORD dwUplinkVolume)
{
    g_dwCacheUplinkVolume = dwUplinkVolume;
    return S_OK;
}

HRESULT PDD_GetUplinkVolume(__out UINT& uiUplinkVolume)
{
   uiUplinkVolume = g_dwCacheUplinkVolume;
   return S_OK;
}

BOOL CResponseTI::ParseCallProgressInformation(LPCSTR& rszPointer)
{
    RILCALLINFO rci;
    LPCSTR szDummy;
    UINT nCid, nMsgType, nIbt, nTch, nDir, nMode, nType, nReason, nLine;
    char szAddress[MAXLENGTH_ADDRESS];
    BOOL fRet = FALSE, fHaveAddress = FALSE;

   // Default values for our processing status. If we recognize the correct status below, we will change these two state.
    m_fUnrecognized = TRUE;
    m_fUnsolicited = TRUE;
    
    // Parse "<cId>,<msgType>,<ibt>,<tch>"
    if (!FindRspPostfix(rszPointer, szDummy)                ||
        !ParseUInt(rszPointer, FALSE, nCid, rszPointer)     ||
        !MatchStringBeginning(rszPointer, ",", rszPointer)  ||
        !ParseUIntAndVerifyAbove(rszPointer, FALSE, LAST_CPI_ID, nMsgType, rszPointer) ||
        !MatchStringBeginning(rszPointer, ",", rszPointer)  ||
        !ParseUInt(rszPointer, FALSE, nIbt, rszPointer)     ||
        !MatchStringBeginning(rszPointer, ",", rszPointer)  ||
        !ParseUInt(rszPointer, FALSE, nTch, rszPointer))
    {
        goto Error;
    }

    // Throw away any unrecognized notificaitons
    if (nMsgType >= LAST_CPI_ID)
    {
        m_fUnrecognized = TRUE;
        fRet = TRUE;
        goto Error;
    }

    memset(&rci, 0x00, sizeof(RILCALLINFO));
    rci.cbSize = sizeof(RILCALLINFO);
    rci.raAddress.cbSize = sizeof(RILADDRESS);

    // default status set to RIL_CPISTAT_UNKNOWN by above memset

    // Set <cId>
    rci.dwID = nCid;
    rci.dwParams |= RIL_PARAM_CI_ID;

    // <ibt> (in-band tones) is ignored
    // <tch> (TCH assignment) is used to reject premature disconnect messages (see below)

    // remaining parameters are all optional with commas used to separate vars even if not
    // present. So if a "," is followed immediately by another "," the var is missing
    
    // ",[<dir>],[<mode>],[<number>],[<type>],[<alpha>],[<cause>],[<line>]" is optionally present

    // Parse "," and look for <dir>
    if(MatchStringBeginning(rszPointer, ",", rszPointer))  // get the first comma - if not there then we are done
    {
        // ignore error - if value not found we just skip and look fo the next comma
        ParseUInt(rszPointer, FALSE, nDir, rszPointer);
    }
    // Parse "," and look for <mode>
    if(MatchStringBeginning(rszPointer, ",", rszPointer))
    {
        nMode = 0;
        ParseUInt(rszPointer, NUM_CALLTYPES, nMode, rszPointer);
        if(nMode > NUM_CALLTYPES)
            goto Error;
    }

    // Set <dir>
    rci.dwDirection = nDir ? RIL_CALLDIR_INCOMING : RIL_CALLDIR_OUTGOING;
    rci.dwParams |= RIL_PARAM_CI_DIRECTION;

    // celltsp requirements:
    //
    // - there cannot be more than one RIL_CPISTAT_xxx notification of the same type for the same call
    // - a RIL_CPISTAT_xxx notification should be sent only after a call is in true xxx state
    //   (eg. RIL_CPISTAT_DISCONNECTED is sent up when we receieve TI_CPISTAT_RELEASE because that is
    //    when the call is actually released. If an attempt to dial is made before getting TI_CPISTAT_RELEASE,
    //    the call could fail. We do capture the disconnect code when we receive TI_CPISTAT_DISCONNECTED
    //    so that it can be used when we send up RIL_CPISTAT_DISCONNECTED later on)
    // - RIL_NOTIFY_CALLPROGRESSINFO can be one of:
    //    RIL_CPISTAT_UNKNOWN - celltsp ignores this
    //    RIL_CPISTAT_NEW_OUTGOING - required by celltsp for MO calls to learn RIL call id
    //    RIL_CPISTAT_NEW_INCOMING - required by celltsp for MT calls to learn RIL call id
    //    RIL_CPISTAT_CONNECTED - required by celltsp when a call goes into a true connected state
    //    RIL_CPISTAT_DISCONNECTED - required by celltsp when a call goes into a true disconnected state
    //    RIL_CPISTAT_ONHOLD - required by celltsp when a call goes into a true hold state
    //
    // celltsp RIL message sequence (only showing radio and corresponding RIL notifications):
    //
    // - MO successful setup
    //    TI_CPISTAT_CALLPROCEED -> RIL_CPISTAT_NEW_OUTGOING (with RIL call id)
    //    TI_CPISTAT_SYNCH -> (ignored)
    //    TI_CPISTAT_ALERT -> (ignored) 
    //    TI_CPISTAT_CONNECTED -> RIL_CPISTAT_CONNECTED
    // - MO failed setup
    //    TI_CPISTAT_CALLPROCEED -> RIL_CPISTAT_NEW_OUTGOING (with RIL call id) 
    //    TI_CPISTAT_SYNCH -> (ignored)
    //    TI_CPISTAT_ALERT -> (ignored)
    //    TI_CPISTAT_DISCONNECT -> (cache disconnect code)
    //    TI_CPISTAT_RELEASE -> RIL_CPISTAT_DISCONNECTED (with cached disconnect code) 
    // - MT successful setup
    //    TI_CPISTAT_SETUP -> (ignored)
    //    V.25 code 2 (ring notification) -> RIL_NOTIFY_RING
    //    TI_CPISTAT_SYNCH -> RIL_CPISTAT_NEW_INCOMING (with RIL call id)
    //    TI_CPISTAT_CONNECTED -> RIL_CPISTAT_CONNECTED 
    // - MT successful setup during active call (call waiting)
    //    TI_CPISTAT_SETUP -> (ignored)
    //    CCWA notification -> RIL_NOTIFY_CALLWAITING
    //    (RIL call id learned by CLCC request/response)
    //    TI_CPISTAT_CONNECTED -> RIL_CPISTAT_CONNECTED
    // - Call teardown
    //    TI_CPISTAT_DISCONNECT -> (cache disconnect code)
    //    TI_CPISTAT_RELEASE -> RIL_CPISTAT_DISCONNECTED (with cached disconnect code) 
    
    /*
     By default, we don't set the RIL_PARAM_CI_CPISTATUS bit. It is set for states that celltsp will recognize. The global call
     state list must be udpated.
    */
    switch ( nMsgType )
    {
        case TI_CPISTAT_CALLPROCEED:
            rci.dwStatus = RIL_CPISTAT_NEW_OUTGOING;
            rci.dwParams |= RIL_PARAM_CI_CPISTATUS;
            EnterCriticalSection(&g_csCallStates); 
            g_rgfCallStates[ rci.dwID ].dwDisconnectCode = 0; // Remove any stale disconnect data.
            g_rgfCallStates[ rci.dwID ].dwParams &= ~RIL_PARAM_CI_DISCONNECTCODE;
            LeaveCriticalSection(&g_csCallStates); 
            break;              
  
        case TI_CPISTAT_SYNCH:
            if ( RIL_CALLDIR_INCOMING == rci.dwDirection )
                {
                rci.dwStatus = RIL_CPISTAT_NEW_INCOMING;
                rci.dwParams |= RIL_PARAM_CI_CPISTATUS;
                EnterCriticalSection(&g_csCallStates); 
                g_rgfCallStates[ rci.dwID ].dwDisconnectCode = 0; // Remove any stale disconnect data.
                g_rgfCallStates[ rci.dwID ].dwParams &= ~RIL_PARAM_CI_DISCONNECTCODE;
                LeaveCriticalSection(&g_csCallStates); 
                }
            break;   
             
        case TI_CPISTAT_CONNECTED:
            rci.dwStatus = RIL_CPISTAT_CONNECTED;
            rci.dwParams |= RIL_PARAM_CI_CPISTATUS;
            break;  
              
        case TI_CPISTAT_RELEASE:
        case TI_CPISTAT_REJECT:
            rci.dwStatus = RIL_CPISTAT_DISCONNECTED;
            rci.dwParams |= RIL_PARAM_CI_CPISTATUS;
            if ( g_rgfCallStates[ rci.dwID ].dwDisconnectCode ) // see if a disconnect code has been cached prior to this
            {
                rci.dwDisconnectCode = g_rgfCallStates[ rci.dwID ].dwDisconnectCode; // copy over the cached value into this rci struct
                rci.dwParams |= RIL_PARAM_CI_DISCONNECTCODE;
            }
            break;    
            
        case TI_CPISTAT_DISCONNECTED:
            // Don't set the RIL_PARAM_CI_CPISTATUS because we have to wait for the RELEASE or REJECT. Do reflect that we have
            // a disconnect code so that we can copy it to our global structure later on.
            // set a default disconnect code. This is used when we disconnect locally
            rci.dwDisconnectCode = RIL_RESULT_CALLABORTED;
            rci.dwParams |= RIL_PARAM_CI_DISCONNECTCODE;
            break;

        case TI_CPISTAT_ALERT:
        case TI_CPISTAT_SETUP:
        case TI_CPISTAT_PROGRESS:
        case TI_CPISTAT_MOSETUP:
        default:
            break;
    }

    // Set <mode>
    rci.dwType = g_rgdwCallTypes[nMode];
    rci.dwParams |= RIL_PARAM_CI_TYPE;

    // Parse "," and look for <address>
    if (MatchStringBeginning(rszPointer, ",", rszPointer))
    {
            if(ParseString(rszPointer, szAddress, MAXLENGTH_ADDRESS, rszPointer))
                 fHaveAddress = TRUE;
    }
    // Parse "," and look for <type>
    if (MatchStringBeginning(rszPointer, ",", rszPointer))
    {
        nType = 0;
        if(ParseUInt(rszPointer, FALSE, nType, rszPointer))
        {
            if(( nType < 0x100) && fHaveAddress) 
            {            
                 if(StringToRILAddress(szAddress, (BYTE)nType, rci.raAddress))
                {
                   rci.dwParams |= RIL_PARAM_CI_ADDRESS;
                   DEBUGMSG(ZONE_ATCMD, (TEXT("RILDrv : i : Parse CPI: found address and type \r\n")));
                }
                else
                    goto Error;
            }
        }
    }
    // Parse "," and look for <alpha>
#ifdef ALPHA_IN_CPI
    if (MatchStringBeginning(rszPointer, ",", rszPointer))
    {
        if(ParseQuotedEncodedString(g_rppPDDParams->etEncodingTECharset, rszPointer, rci.wszDescription, rci.wszDescription + MAXLENGTH_DESCRIPTION))
        {
            rci.dwParams |= RIL_PARAM_CI_DESCRIPTION;
        }
    }
#endif
    // Parse "," and look for the disconnect reason for extended CPI notifications
    if (MatchStringBeginning(rszPointer, ",", rszPointer))
    {
        if(ParseUInt(rszPointer, FALSE, nReason, rszPointer))
        {
            DEBUGMSG(ZONE_ATCMD, (TEXT("RILDrv : i : Parse CPI: got reason code %d\r\n"), nReason));
            if(TI_CPISTAT_DISCONNECTED == nMsgType)
            {
                // numeric codes below are from GSM 3G TS 24.008  Annex H
                // for now we only process busy
                if(17 == nReason)
                {
                    rci.dwDisconnectCode = RIL_RESULT_BUSY;
                    rci.dwParams |= RIL_PARAM_CI_DISCONNECTCODE;
                }
                else if(16 != nReason)
                {
                    rci.dwDisconnectCode = RIL_RESULT_NOCARRIER;  // add reject code
                    rci.dwParams |= RIL_PARAM_CI_DISCONNECTCODE;
                }
                DEBUGMSG(ZONE_ATCMD, (TEXT("RILDrv : i : Parse CPI: disconnect code %d\r\n"), rci.dwDisconnectCode));
            }
        }
    }
    // Parse "," and look for <line>
    if (MatchStringBeginning(rszPointer, ",", rszPointer))
    {
        if(!ParseUInt(rszPointer, FALSE, nLine, rszPointer))
            goto Error;
    }
    
    if (g_rfExternalCalltypeDetermination)
    {
        SetCalltypeFromCallInfo(&rci);
    }

    // add the call to the callstates array
    DEBUGCHK((0 <= rci.dwID) && (rci.dwID < RIL_MAX_TRACKED_CALL_ID));  // Otherwise, it may be necessary to increase MAX_TRACKED_CALLS
    // The global list is updated if we have either a recognized status or we have collected a disconnect.
    if ( (rci.dwParams & RIL_PARAM_CI_CPISTATUS) || (rci.dwParams & RIL_PARAM_CI_DISCONNECTCODE))
    {
        EnterCriticalSection(&g_csCallStates); 
        g_rgfCallStates[rci.dwID] = rci;
        LeaveCriticalSection(&g_csCallStates); 

        // Indicate call is inactive to audio driver only if call list is empty
        IndicateCallActivityToAudioSubsystem (FALSE, TRUE);
    
        if ( rci.dwParams & RIL_PARAM_CI_CPISTATUS )
        {
            DEBUGMSG(ZONE_ATCMD, (TEXT("RILDrv : i : Parse CPI: sending notification type %d\r\n"), nMsgType));
            m_fUnsolicited = TRUE;
            m_fUnrecognized = FALSE;
            m_dwCode = RIL_NOTIFY_CALLPROGRESSINFO;
            if (!SetBlob((void*)&rci, sizeof(RILCALLINFO)))    
                goto Error;

            // Special case. If we are establishing a new call and one is already active, query CLCC to see it it is held.
            if ( RIL_CPISTAT_NEW_OUTGOING == rci.dwStatus)
            {
                for (uint GlobalCallStateIndex = 1 /* first valid call ID */; GlobalCallStateIndex < RIL_MAX_TRACKED_CALL_ID; GlobalCallStateIndex++ )
                {
                    if (g_rgfCallStates[GlobalCallStateIndex].dwID &&  
                       (g_rgfCallStates[GlobalCallStateIndex].dwParams & RIL_PARAM_CI_CPISTATUS) && 
                       (RIL_CPISTAT_CONNECTED == g_rgfCallStates[GlobalCallStateIndex].dwStatus))
                    {
                        SetupCallListEvaluation ();
                        break;
                    }
                }
            }
        }

    }


    fRet = TRUE;

Error:

   /* The following example will cause problems down the road:
      %CPI: 1,0,0,1,1,0,"14257272470<cr><lf>
      Notice the missing end quote. In this case the check for CRLF will fail in if (!ParseRspPostfix(szPointer, szPointer)).
      If the check is performed here, the pointer can be placed to point at the next CRLF to avoid the failure. 

      In another case, we made it here because we did parse a CPI, but the total command is not correct yet. It is possible that the 
      response is complete (contains a cr lf, but the contents are invalid. In this case, the command should be purged; otherwise,
      it will block subsequent notification processing.
   */
    if (!ParseRspPostfix(rszPointer, szDummy))
    {
        if (FindRspPostfix(rszPointer, rszPointer))
        {
            DEBUGMSG(ZONE_ATCMD, (TEXT("RILDrv : i : Parse CPI: fix up <cr><lf> \r\n")));
            rszPointer -= 2; // Length of "\r\n"
            fRet = TRUE;
        }
    }

    return fRet;
}

BOOL CResponseTI::ParseNotificationOEM
(
    LPCSTR& szPointer,
    BOOL& fExpectCRLF,
    BOOL fDataOnNotificationPort
#ifdef RIL_WATSON_REPORT    
    , BOOL& fErrorNotification
#endif
)
{
    BOOL fRetVal = TRUE;

    if (MatchStringBeginning(szPointer, "CONNECT 9600", szPointer))
    { 
        if (!ParseV25Response(15))
        {
            fRetVal = FALSE;
            goto Error;
        }
        fExpectCRLF = FALSE;
    }
    else if (MatchStringAnywhere(szPointer, "AT-Command Interpreter ready", szPointer))
    {
        m_fUnsolicited = FALSE;
        g_fSignalQualityReceived = FALSE;

#ifdef RIL_RADIO_RESILIENCE
        // If this isn't the initial message sent on boot, then it is a radio reset
        if (g_fInitedFirstTime)
        {
            SignalCriticalError(RILLOG_EVENT_GENERALREINIT, __LINE__, __FILE__);        
            MakeError(RIL_E_RADIOREBOOTED);
        }
#endif // RIL_RADIO_RESILIENCE
    }
    else if (MatchStringAnywhere(szPointer, "%CPI: ", szPointer))
    {
        if (!ParseCallProgressInformation(szPointer))
        {
            fRetVal = FALSE;
            goto Error;
        }
    }
    else if (MatchStringBeginning(szPointer, "%CSQ: ", szPointer))
    {        
        LPCSTR szPostfix;
        if (!FindRspPostfix(szPointer, szPostfix))
        {
            // This isn't a complete signal quality notification -- no need to parse it
            fRetVal = FALSE;
            goto Error;
        }
        if (!g_fSignalQualityReceived)
        {
            memset(&g_rsq, 0x00, sizeof(RILSIGNALQUALITY));
        }
        g_rsq.cbSize = sizeof(RILSIGNALQUALITY);
        // OEM1 can provide signal strength notifications
        if (!ParseSignalQualityData(szPointer, &g_rsq, szPointer))
        {
            fRetVal = FALSE;
            goto Error;
        }
        m_fUnsolicited = TRUE;
        m_dwCode = RIL_NOTIFY_SIGNALQUALITY;
        if (!SetBlob((void*)&g_rsq, sizeof(RILSIGNALQUALITY)))
        {
            fRetVal = FALSE;
            goto Error;
        }   
        g_fSignalQualityReceived = TRUE;
    }
    // HW-SPECIFIC:OEM1 hardware issues a CSTAT notification when the SIM PB is ready or SMS store is ready
    else if (MatchStringBeginning(szPointer, "%CSTAT: ", szPointer))
    {
        RILEQUIPMENTSTATE res;
        UINT  nStatus;
        DWORD dwEntity = 0;
        BOOL fReady = FALSE;

        // Parse the following integer to se which state the SIM has reached
        if(MatchStringBeginning(szPointer, "PHB", szPointer))
            dwEntity = RIL_READYSTATE_SIM_PB;
        else if(MatchStringBeginning(szPointer, "SMS", szPointer))
            dwEntity = RIL_READYSTATE_SMS;
        else if(MatchStringBeginning(szPointer, "RDY", szPointer))
        {
            dwEntity = RIL_READYSTATE_SIM_PB | RIL_READYSTATE_SMS;
            fReady = TRUE;
        }

        if(!MatchStringBeginning(szPointer, ",", szPointer)  ||
           !ParseUInt(szPointer, FALSE, nStatus, szPointer))
        {
            fRetVal = FALSE;
            goto Error;
        }
        if((1 != nStatus)  || (0 == dwEntity))  // if report is Not Ready - then just ignore it
        {
            fRetVal = FALSE;
            goto Error;
        }

        if ( fReady )
        {
            /* radio is ready; read required SIM files */
            StartSIMRecordReadThread();
        }

        // send the remaining init commands the first time we are told SMS is ready
        if((dwEntity & RIL_READYSTATE_SMS) && !(g_dwReadyState & RIL_READYSTATE_SMS))
            (void)SendComInitString(COM_SMSINIT_INDEX);

        g_dwReadyState |= dwEntity;    
        m_fUnsolicited = TRUE;
        m_dwCode = RIL_NOTIFY_RADIOEQUIPMENTSTATECHANGED;
        memset(&res, 0x00, sizeof(RILEQUIPMENTSTATE));
        res.cbSize = sizeof(RILEQUIPMENTSTATE);
        res.dwParams = RIL_PARAM_EQUIPMENTSTATE_READYSTATE;
        res.dwReadyState = g_dwReadyState;
        if (!SetBlob((void*)(&res), res.cbSize))
        {
            fRetVal = FALSE;
            goto Error;
        }
    }
    // HW-SPECIFIC:OEM1 hardware issues a CTZV network date/time notification.
    else if (MatchStringBeginning(szPointer, "%CTZV: \"", szPointer))
    {
        UINT dwDate[3];
        UINT dwTime[3];
        UINT dwOffset;
        BOOL fPass = FALSE;
        BOOL fNegative = FALSE;
        RILNITZINFO RilNitzInfo;

        memset (&RilNitzInfo, 0, sizeof(RilNitzInfo));
        RilNitzInfo.cbSize = sizeof(RilNitzInfo);
        RilNitzInfo.dwNotificationCode = RIL_NOTIFY_NITZ;
        RilNitzInfo.dwParams = RIL_PARAM_NITZ_SYSTEMTIME | RIL_PARAM_NITZ_TIMEZONEOFFSET;


        for(UINT i=0; i<3; i++ )
        {
            if(ParseUInt(szPointer, TRUE, dwDate[i], szPointer))
            {
                if (MatchStringBeginning(szPointer, "/", szPointer))
                {
                    continue;
                }
                else
                {
                    if ( 2 == i ) fPass = TRUE;
                    break;
                }
            }
        }

        if ( TRUE == fPass )
        {
            RilNitzInfo.SysTime.wYear = dwDate[0];
            RilNitzInfo.SysTime.wMonth = dwDate[1];
            RilNitzInfo.SysTime.wDay = dwDate[2];
            fPass = FALSE;

            if (MatchStringBeginning(szPointer, ",", szPointer))
            {
                for(UINT i=0; i<3; i++ )
                {
                    if(ParseUInt(szPointer, TRUE, dwTime[i], szPointer))
                    {
                        if (MatchStringBeginning(szPointer, ":", szPointer))
                        {
                            continue;
                        }
                        else
                        {
                            if ( 2 == i ) fPass = TRUE;
                            break;
                        }
                    }
                }
            }

        }

        if ( TRUE == fPass )
        {
            RilNitzInfo.SysTime.wHour = dwTime[0];
            RilNitzInfo.SysTime.wMinute = dwTime[1];
            RilNitzInfo.SysTime.wSecond = dwTime[2];
            fPass = FALSE;

            if (MatchStringBeginning(szPointer, "-", szPointer))
            {
                fNegative = TRUE;
            }
            else
            {
                MatchStringBeginning(szPointer, "+", szPointer);
            }


            if(ParseUInt(szPointer, TRUE, dwOffset, szPointer))
            {
                fPass = TRUE;
                RilNitzInfo.TimeZoneOffsetMinutes = 60 * (fNegative ? (0-dwOffset):dwOffset);
            }
        }

        if ( TRUE == fPass )
        {
            MatchStringAnywhere(szPointer, "\"", szPointer);

            m_fUnsolicited = TRUE;
            m_dwCode = RIL_NOTIFY_NITZ;
            if (!SetBlob((void*)(&RilNitzInfo), RilNitzInfo.cbSize))
            {
                fRetVal = FALSE;
                goto Error;
            }
        }
        else
        {
            fRetVal = FALSE;
            goto Error;
        }
    }
    // HW-SPECIFIC:OEM1 hardware issues a CGEV notification when the GPRS context state is changed
    // currently only context deactivations by teh network are processed (previously reported by 3 (NO CARRIER))
    else if (MatchStringBeginning(szPointer, "+CGEV: ", szPointer))
    {
        bool fSendingNotification = false;
        RILGPRSCONTEXTACTIVATED rGPRSca;
        memset(&rGPRSca, 0x00, sizeof(RILGPRSCONTEXTACTIVATED));
        rGPRSca.cbSize = sizeof(RILGPRSCONTEXTACTIVATED);

        if (MatchStringBeginning(szPointer, "NW DETACH", szPointer))
        {
            DEBUGMSG(ZONE_ATCMD, (TEXT("RILDrv : i :Parse CGEV: found NW DETACH \r\n")));
            memset(&rGPRSca, 0, sizeof(rGPRSca));
            rGPRSca.cbSize = sizeof(rGPRSca);
            rGPRSca.fActivated = FALSE;
            rGPRSca.dwEvent = RIL_RILGPRSCONTEXTACTIVATED_NWDETACH;
            m_fUnsolicited = TRUE;
            m_dwCode = RIL_NOTIFY_GPRSCONNECTIONSTATUS;
            if (!SetBlob((void*)&rGPRSca, sizeof(RILGPRSCONTEXTACTIVATED)))    {
               fRetVal = FALSE;
               goto Error;
                }
           fSendingNotification = true;
        }
        else if (MatchStringBeginning(szPointer, "NW DEACT", szPointer))
        {
            UINT nCid;
            // format is <PDP_Type, <PDP_Addr>, [<cid>]
            DEBUGMSG(ZONE_ATCMD, (TEXT("RILDrv : i : Parse CGEV: found NW DEACT \r\n")));
            MatchStringBeginning(szPointer, ",", szPointer); // skip over PDP_Type, ignore failure
            MatchStringBeginning(szPointer, ",", szPointer); // and PDP_Addr
            memset(&rGPRSca, 0, sizeof(rGPRSca));
            rGPRSca.cbSize = sizeof(rGPRSca);
            rGPRSca.fActivated = FALSE;
            rGPRSca.dwEvent = RIL_RILGPRSCONTEXTACTIVATED_NWDEACT;
            if(ParseUInt(szPointer, FALSE, nCid, szPointer))
            {
                rGPRSca.dwContextID = nCid;
            }
            else
            {
                rGPRSca.dwContextID = ((DWORD)-1); //unknown call ID
            }
            // currently we send up a disconnect notification regardless of any parsing failures
           m_fUnsolicited = TRUE;
           m_dwCode = RIL_NOTIFY_GPRSCONNECTIONSTATUS;
           if (!SetBlob((void*)&rGPRSca, sizeof(RILGPRSCONTEXTACTIVATED)))    {
               fRetVal = FALSE;
               goto Error;
            }
           fSendingNotification = true;            
        }
        else if (MatchStringBeginning(szPointer, "ME DEACT", szPointer))
        {
            DEBUGMSG(ZONE_ATCMD, (TEXT("RILDrv : i : Parse CGEV: found ME DEACT \r\n")));

            EnterCriticalSection(&g_csGPRSDeactLock); 
            g_fDeactNotificationRcvd = TRUE;
            g_LastDeactNotificationRcvd = GetTickCount();
            LeaveCriticalSection(&g_csGPRSDeactLock); 
                
            UINT nCid;
            // format is <PDP_Type, <PDP_Addr>, [<cid>]
            // +CGEV: ME DEACT "IP","010.172.070.192",1<cr><lf>
            
            MatchStringAnywhere(szPointer, ",", szPointer); // skip over PDP_Type, ignore failure
            MatchStringAnywhere(szPointer, ",", szPointer); // and PDP_Addr
            if(ParseUInt(szPointer, FALSE, nCid, szPointer))
            {
                rGPRSca.dwContextID = nCid;
            }
            else
            {
                rGPRSca.dwContextID = ((DWORD)-1); //unknown call ID
            }
            DEBUGMSG(ZONE_ATCMD, (TEXT("RILDrv : i : Parse CGEV: After parsing context id, cid = [%d]\r\n"), nCid));
          
            // Even though this came as a notification from the radio, fake this to be a result
            // by setting m_fUnsolicited to FALSE (i.e. solicited)
            
            m_fUnsolicited = FALSE;
            m_dwCode = RIL_NOTIFY_GPRSCONNECTIONSTATUS;
            if (!SetBlob((void*)&rGPRSca, sizeof(RILGPRSCONTEXTACTIVATED)))    {
                fRetVal = FALSE;
                goto Error;
                }
            fSendingNotification = true;
        }
        else if (MatchStringBeginning(szPointer, "ME DETACH", szPointer))
        {
            DEBUGMSG(ZONE_ATCMD, (TEXT("RILDrv : i : Parse CGEV: found ME DETACH \r\n")));
        }
        else if (MatchStringBeginning(szPointer, "NW REACT", szPointer))
        {
            DEBUGMSG(ZONE_ATCMD, (TEXT("RILDrv : i : Parse CGEV: found NW REACT \r\n")));
        }
        // make sure the upper levels know that we discarded this
        if(!fSendingNotification)
            ParseJunk(1, szPointer);
     }
    // HW-SPECIFIC: OEM1 driver issues custom %SATI, %SATA, and %SATN notifications for SIM Toolkit events
    else if (MatchStringBeginning(szPointer, "%SATI: ", szPointer))
    {
        // Unhandled SIM Toolkit command notification
        if (!ParseSIMToolkitCmdOrRsp(szPointer, RIL_NOTIFY_SIMTOOLKITCMD))
        {
            fRetVal = FALSE;
            goto Error;
        }
    }
    else if (MatchStringBeginning(szPointer, "%SATN: ", szPointer))
    {
        // Unhandled SIM Toolkit command or response notification
        if (!ParseSIMToolkitCmdOrRsp(szPointer, RIL_NOTIFY_SIMTOOLKITEVENT))
        {
            fRetVal = FALSE;
            goto Error;
        }
    }
    else if (MatchStringBeginning(szPointer, "%SATA:", szPointer))
    {
        // SIMToolkit SetupCall notification
        if (!ParseSIMToolkitCallSetup(szPointer))
        {
            fRetVal = FALSE;
            goto Error;
        }
    }
    else if (MatchStringBeginning(szPointer, "%SATE: ", szPointer))
    {
        // The %SATE response occurs only for SETUP MENU REPLY and is not needed.
        // The real data we are interested in will be in the upcoming %SATI notification.
        ParseJunk(1, szPointer);
    }
    else if (MatchStringBeginning(szPointer, "+EXT ERROR: ", szPointer))
    {
        UINT nCode = 0;
        
        if (!ParseExtError(szPointer, NULL, 0, rppTIPDDParams.pemEXTErrorTable, rppTIPDDParams.uiEXTErrorTableSize, nCode, TRUE))
        {
            DEBUGMSG(ZONE_ATCMD, (TEXT("RILDrv : i : Unrecognized EXT Error response\r\n")));
            fRetVal = FALSE;
            goto Error;
        }
#ifdef RIL_LAST_ERROR
        g_dwLastError = MAKELONG(nCode, RADIO_EXTERROR);
#endif
#ifdef RIL_WATSON_REPORT
        EnterCriticalSection(&g_csRilInfoCache);
        strncpyz(g_RilInfoCache.szLastError, szPointer, MAXLENGTH_CMD);
        LeaveCriticalSection(&g_csRilInfoCache);
        fErrorNotification = true;
#endif // RIL_WATSON_REPORT

        fExpectCRLF = FALSE;
    }
    else
    {
        // Treat the "no match" case as an error
        fRetVal = FALSE;
        goto Error;
    }

Error:
    return fRetVal;
}

BOOL CResponseTI::ParseSIMToolkitCmdOrRspOEM
(
   LPCSTR& rszPointer, // IN, OUT
   const DWORD dwNotifyCode, // IN
   RILSIMTOOLKITCMD*& pCmd, // OUT
   DWORD& dwCmdSize // OUT
)
{
    BYTE* pbDataWalk=NULL;
    LPCSTR szDummy=NULL;
    UINT i=0;
    UINT cbDataString=0;
    LPCSTR pchDataStringWalk=NULL;
    LPSTR szDataString = NULL;
    DWORD dwSIMResponse=0;
    
    BYTE* pbData = NULL;
    UINT cbData = 0;
    CRilSimToolkitCommand rstcCmd;    
    BOOL fRet = FALSE;

    // Determine if data is wrapped in quotes and Parse <data>
    // NOTE: we take ownership of allocated szDataString in both cases
    if ('\"' == *rszPointer)
    {
        // Look for a "<postfix>"
        if (!MatchStringAnywhere(rszPointer, "\"\r\n", szDummy))
        {
            // This isn't a complete SIM Toolkit notification -- no need to parse it
            goto Error;
        }

        if (!ParseUnlimitedString(rszPointer, szDataString, cbDataString, rszPointer))
        {
            goto Error;
        }
    }
    else
    {
        // Look for a "<postfix>"
        if (!FindRspPostfix(rszPointer, szDummy))
        {
            // This isn't a complete SIM Toolkit notification -- no need to parse it
            goto Error;
        }
    
        if (!ParseUnlimitedUnquotedString(rszPointer, '\r', szDataString, cbDataString, rszPointer))
        {
            goto Error;
        }
    }
    
    if ( (szDataString[0]=='\0') && (dwNotifyCode==RIL_NOTIFY_SIMTOOLKITCMD) )
    {
        m_dwCode=RIL_NOTIFY_SIMTOOLKITSESSIONEND;
    }
    else
    {
        DEBUGCHK(0 == (cbDataString - 1) % 2);

        if ( 0 == cbDataString )
        {
            goto Error;
        }
        cbData = (cbDataString - 1) / 2;
        if ( 0 == cbData  )
        {
            goto Error;
        }
        pbData = (BYTE*)AllocBlob(cbData);
        if (!pbData)
        {
            goto Error;
        }

        pbDataWalk = pbData;
        pchDataStringWalk = szDataString;
        for (i = 0; i < cbData; i++)
        {
            if ( (*(pchDataStringWalk + 1) != 0x0a) && (*(pchDataStringWalk + 1) != 0x0d))
            {
                *pbDataWalk = SemiByteCharsToByte(*pchDataStringWalk, *(pchDataStringWalk + 1));
                pbDataWalk++;
            }

            pchDataStringWalk += 2;
        }

        cbData = pbDataWalk - pbData;
        pCmd = NULL;
        dwCmdSize = 0;
        dwSIMResponse = SIM_RESPONSE_OK;
        dwSIMResponse = rstcCmd.ParseCommand((LPSTR)pbData, cbData, &pCmd, &dwCmdSize);

        if (SIM_RESPONSE_OK != dwSIMResponse)
        {
            // Error parsing the command. 
            // There was a critical error if an error notification was not created.
            if (NULL == pCmd || (0 >= dwCmdSize))
            {
                goto Error;
            }

            // With this radio, we occasionally receive notifications of 
            // radio handled responses which we do not want to send a response for.
            if (SIM_RESPONSE_ERR_COMMANDNUMBER != dwSIMResponse)
            {
                // Send a response to the radio
                if (FAILED(SendSIMToolkitErrorRsp(pCmd)))
                {
                    DEBUGCHK(FALSE);
                }
            }
        }
    
        if (!SetBlob((void*)pCmd, dwCmdSize))
        {
            goto Error;
        }
        m_dwCode = dwNotifyCode;
    }

    m_fUnsolicited = TRUE;
    fRet = TRUE;

Error:
    
    delete[] szDataString;
    FreeBlob(pbData);    
    return fRet;
}

void CResponseTI::Pre_ParseOKOrErrorOEM(const char* szPointer)
{
    // Necessary change to make battery driver calls work on this hardware
    if( strstr( szPointer, "ADC" ) )
    {
        LPCSTR pch;

        pch = const_cast<char*>(strstr( szPointer, "\n4\r" ));
        if( pch )
        {
            *(m_szData + (pch - m_szData) +1) = 0x30;
        }
    }
    
    return;
}
static DWORD WINAPI StartSIMRecordReadThreadCB( LPVOID lpThreadData )
{
    StartPlmnNameRead();
#ifdef RIL_ENABLE_EONS
    StartEONSRead();
#endif
    return 0;
}

void StartSIMRecordReadThread()
{
    /* do not block current thread -- do the SIM record read in another thread */
    HANDLE hThread = CreateThread(NULL, 0, &StartSIMRecordReadThreadCB, 0, 0, NULL);
    if ( hThread != NULL )
    {
        CloseHandle( hThread );
        hThread = NULL;
    }
}


HRESULT PDD_CreateCommand_GetATR(__out_ecount(cchCmd) LPSTR szCmd, __in const size_t cchCmd)
{
   return StringCchCopyA( szCmd, cchCmd, "AT%ATR?\r" );
}

//
//
//
static HRESULT ParseATRInfo(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseATRInfo);
    
    HRESULT hr = S_OK;
    RILATRINFO* pATRInfo = NULL;
    UINT uiPhase;
    UINT uiCurATRChar;
    UINT cbATRString;
    UINT cbATRData;
    LPSTR szATRString = NULL;
    LPCSTR pchATRStringWalk;
    BYTE* pbATRDataWalk;
    
    
    // Check parameters
    if (NULL == szRsp)
    {
        DEBUGCHK(FALSE);
        return E_INVALIDARG;
    }

    // Initialize output parameters
    pBlob = NULL;
    cbBlob = 0;

    pATRInfo = (RILATRINFO*)AllocBlob(sizeof(RILATRINFO));
    if (NULL == pATRInfo)
    {
        DEBUGCHK(FALSE);
        hr = E_OUTOFMEMORY;
        goto Error;
    }

    memset(pATRInfo, 0, sizeof(RILATRINFO));
    pATRInfo->cbSize = sizeof(RILATRINFO);
    
    // Parse "<prefix>%ATR: <Phase>,<ATR>"
    // We take ownership of szATRString and are responsible for freeing the memory.
    if (!ParseRspPrefix(szRsp, szRsp)                 ||
        !MatchStringBeginning(szRsp, "%ATR: ", szRsp) ||
        !ParseHexUInt(szRsp, TRUE, uiPhase, szRsp)    ||
        !MatchStringBeginning(szRsp, ",", szRsp)      ||
        !ParseUnlimitedUnquotedString(szRsp, '\r', szATRString, cbATRString, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Assert that the data we received is an even length.
    DEBUGCHK(0 == (cbATRString-1) % 2);

    cbATRData = (cbATRString-1) / 2;
    pchATRStringWalk = szATRString;
    pbATRDataWalk = pATRInfo->rgbATR;
    
    // Check that the data is not larger than the space available in the structure.
    if (cbATRData > ARRAY_LENGTH(pATRInfo->rgbATR))
    {
        DEBUGCHK(!"ATR Data is larger than the structure buffer.");
        hr = E_FAIL;
        goto Error;
    }

    // Walk through the ATR string and convert to bytes.
    for (uiCurATRChar = 0 ; uiCurATRChar < cbATRData ; uiCurATRChar++)
    {
        *pbATRDataWalk = SemiByteCharsToByte(*pchATRStringWalk, *(pchATRStringWalk + 1));
        pbATRDataWalk++;
        pchATRStringWalk += 2;
    }


    // Everything is parsed, finish setting all the fields of the structure.
    pATRInfo->cbATRSize = cbATRData;
    pATRInfo->dwPhase = uiPhase;
    pATRInfo->dwParams = RIL_PARAM_ATR_ALL;

    // Everything is finished. Assign the results to the output parameters        
    pBlob = pATRInfo;
    cbBlob = sizeof(RILATRINFO);

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

HRESULT PDD_CreateCommand_TerminateSimToolkitSession(__out_ecount(cchCmd) LPSTR szCmd, __in const size_t cchCmd, __in const DWORD dwCause)
{
   return StringCchPrintfA( szCmd, cchCmd, "AT%%SATT=%u\r", dwCause );
}

HRESULT PDD_CreateCommand_EnableSignalQualityNotification(__out_ecount(cchCmd) LPSTR szCmd, __in const size_t cchCmd, __in const bool fEnable)
{
   return StringCchPrintfA( szCmd, cchCmd, "AT%%CSQ=%u\r", (fEnable?1:0) );
}

// NDIS stuff
HRESULT PDD_GetRilNdisObject(__in const CRilHandle* pRilHandle, __out CRilNDIS*& pRilNdis)
{
    //Not supported
    return E_NOTIMPL;
}

HRESULT PDD_GetRilInstanceNdisObject(__in const CRilInstanceHandle* pRilInstanceHandle, __out CRilInstanceNDIS*& pRilInstanceNdis)
{
    //Not supported
    return E_NOTIMPL;
}


static HRESULT ParseGetNITZSupported( __in LPCSTR szRsp, __out void*& pBlob, __out UINT& cbBlob )
{
    FUNCTION_TRACE(ParseGetNITZSupported);
    UINT nValue;
    HRESULT hr = E_FAIL;
    DWORD * pdwNITZNotificationSetting = NULL;


    pBlob = NULL;
    cbBlob = 0;

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

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

    // Parse "%CTZV: "
    if (MatchStringBeginning(szRsp, "%CTZV: ", szRsp)) 
    {
        if (ParseUInt(szRsp, FALSE, nValue, szRsp))
        {
            hr = S_OK;
            if ( 0 == nValue )
            {
                *pdwNITZNotificationSetting = RIL_CAPS_NITZ_DISABLED;
            }
            else
            {
                *pdwNITZNotificationSetting = RIL_CAPS_NITZ_ENABLED;
            }
            pBlob = (void*)pdwNITZNotificationSetting;
            cbBlob = sizeof(DWORD);
        }
    }

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

