/*****************************************************************************
 * Copyright (C) Gemalto M2M GmbH
 *****************************************************************************
 * Project: IMC-RIL-Driver
 *****************************************************************************
 * Author : Wireless Modules RD SW A2
 *****************************************************************************
 * $Workfile: Y:/z_ms-d/src/working/Quinn/RilDrv/caps.cpp $
 * $Revision: /main/ms-d/44 $
 *     $Date: 2013-02-26 15:07:28 $
 *   $Author: pmackent $
 *****************************************************************************
 * Target:
 *  Microsoft Windows Mobile 5.0
 *****************************************************************************
 * Description:
 *  Gemalto IMC RIL
 *****************************************************************************
 *
 * $Log: Y:/z_ms-d/src/working/Quinn/RilDrv/caps.cpp$ 
 * 
 * /main/ms-d/44   2013-02-26 15:07:28   pmackent 
 * Adaptions for EHS5 1.290
 *
 * /main/ms-d/43   2013-01-07 10:14:48   pmackent
 * Workaround for exit in AT+CPMS in EHS5
 *
 * /main/ms-d/42   2012-11-02 16:31:29   pmackent
 * Added missing lock capabilities PIN2 and FD
 *
 * /main/ms-d/41   2012-08-09 13:46:29   pmackent
 * Fix potential memory leaks
 *
 * /main/ms-d/40   2012-04-18 13:46:46   ctna0256
 * Introducing PVS8
 * Implementing CDMA-SMS
 * Various CDMA enhancements
 *
 * /main/ms-d/39   2012-02-27 15:27:31   ctna0256
 * Skip optional parameters of AT+CPBR=? command
 *
 * /main/ms-d/38   2012-02-22 13:26:27   ctna0256
 * Encapsulate use of Intel AT command set
 *
 * /main/ms-d/37   2012-02-03 14:01:04   ctna0256
 * Phonebook and SIM access now in separate files
 *
 * /main/ms-d/36   2012-01-31 16:38:16   ctna0256
 * Added support for EHS5
 *
 * /main/ms-d/35   2012-01-12 15:22:56   ctna0256
 * Some basic CDMA adaptions
 *
 * /main/ms-d/34   2012-01-10 13:30:22   ctna0450
 * Add "Phone SIM Lock" capability to RIL_CAPSTYPE_LOCKFACILITIES to fix problem with PIN dialog on SIM locked phone (IPIS100076277)
 *
 * /main/ms-d/33   2011-09-26 13:34:09   ctna0256
 * Comply with CE6 warning level 4
 *
 * /main/ms-d/32   2011-09-19 10:26:24   ctna0256
 * Workaround for wrong CME-Error at AT+CPOL on PH8
 *
 * /main/ms-d/31   2011-07-27 08:56:28   ctna0256
 * Change default for notification capabilities for CE6
 *
 * /main/ms-d/30   2011-03-09 14:24:31   ctna0256
 * Run code analysis on Windows CE7
 *
 * /main/ms-d/29   2010-12-03 11:12:16   ctna0256
 * Workaround for AT+CPOL=? response on wrong interface
 *
 * /main/ms-d/28   2010-09-21 13:12:56   ctna0256
 * No phonebook access after enabling PIN on PH8 customer sample
 *
 * /main/ms-d/27   2010-08-10 09:13:52   ctna0256
 * Better support of modules without phonebook support
 *
 * /main/ms-d/26   2010-02-03 10:50:42   ctna0256
 * Introduction of WinCE6 support
 *
 * /main/ms-d/25   2009-11-17 13:18:47   ctna0256
 * Correct error code for NITZ capability (IPIS100060279)
 *
 * /main/ms-d/24   2009-03-19 10:57:52   ctna0256
 * WM65 capabilities
 *
 * /main/ms-d/23   2009-02-23 13:15:55   ctna0256
 * Avoid unwanted E_FAIL error codes
 *
 * /main/ms-d/22   2008-10-06 09:24:55   ctna0256
 * Remove support of extended phonebook
 *
 * /main/ms-d/21   2008-09-19 16:51:24   ctna0256
 * Corrected misplaced "else"
 *
 * /main/ms-d/20   2008-09-19 16:39:52   ctna0256
 * New registry key "SupportPBEx"
 *
 * /main/ms-d/19   2008-09-18 15:24:40   ctna0256
 * Introduction of extended phonebook support in WM6  AKU 1.3
 *
 * /main/ms-d/18   2008-09-17 11:06:01   ctna0256
 * Handle syntax of extended phonebook commands
 *
 * /main/ms-d/17   2008-08-22 10:22:48   ctna0256
 * Set Cinterion copyright text
 *
 * /main/ms-d/16   2008-08-21 09:34:00   ctna0256
 * Prepared for new disclaimer handling
 *
 * /main/ms-d/15   2008-06-20 11:05:39   mp49210
 * Remove unused SIEMENS_MAGNETO define
 *
 * /main/ms-d/14   2008-04-29 13:33:15   mp49210
 * Avoid sending test commands when module isn't ready
 *
 * /main/ms-d/13   2008-02-28 13:10:40   mp49210
 * Correct use of tone duration capability
 *
 * /main/ms-d/12   2008-01-10 16:05:54   mp49210
 * IPIS100044562: report empty list instead of error when query for SIM phonebooks fails
 *
 * /main/ms-d/11   2007-09-21 10:29:39   mp49210
 * Introduction WM6 specific AcT selection (optional)
 *
 * /main/ms-d/10   2007-09-20 12:10:40   mp49210
 * No useful SIM phonebooks on HC25/HC28 customer samples
 *
 * /main/ms-d/9   2007-05-03 14:55:51   mp49210
 * Workaround for problem with AT+CPOL=? in HC25
 *
 * /main/ms-d/8   2007-04-04 16:27:47   mp49210
 * Removed unnecessary retries for At+CPOL=?
 *
 * /main/ms-d/7   2007-03-28 12:03:19   mp49210
 * Added new parameters for AT+CBST and AT+CRLP
 *
 * /main/ms-d/6   2006-12-15 17:12:44   mp49210
 * Update calll management capabilities.
 * Prevent 2G/3G switch while active call.
 *
 * /main/ms-d/5   2006-11-17 14:25:26   mp49210
 * Different AT+CLCK facilities
 *
 * /main/ms-d/4   2006-11-13 11:40:21   mp49210
 * Use AT+CBST and AT+CRLP only on data port.
 *
 * /main/ms-d/3   2006-11-10 11:58:28   mp49210
 * Send AT+CPOL=? on URC port only
 *
 * /main/ms-d/2   2006-11-07 10:18:45   mp49210
 * Substitute AT^SPLR and AT^SPLW
 *
 * /main/ms-d/1   2006-10-25 16:52:54   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 "rilhand.h" //HGJ add for debug

//
// NOTE: all the following mapping arrays must be kept sorted in the left column
//       (this is needed, because they're used for binary search)
//

//
// Call management commands
//
//static const LISTVALUEMAP g_rglvmCapsCallCmds[] =
//{
//    { 0,    RIL_CAPS_CALLCMD_RELEASEHELD              },
//    { 1,    RIL_CAPS_CALLCMD_RELEASEACTIVE_ACCEPTHELD },
//    { 2,    RIL_CAPS_CALLCMD_HOLDACTIVE_ACCEPTHELD    },
//    { 3,    RIL_CAPS_CALLCMD_ADDHELDTOCONF            },
//    { 4,    RIL_CAPS_CALLCMD_ADDHELDTOCONF_DISCONNECT },
//    { 5,    RIL_CAPS_CALLCMD_INVOKECCBS               },
//    { 11,   RIL_CAPS_CALLCMD_RELEASECALL              },
//    { 12,   RIL_CAPS_CALLCMD_RELEASECALL              },
//    { 13,   RIL_CAPS_CALLCMD_RELEASECALL              },
//    { 14,   RIL_CAPS_CALLCMD_RELEASECALL              },
//    { 15,   RIL_CAPS_CALLCMD_RELEASECALL              },
//    { 16,   RIL_CAPS_CALLCMD_RELEASECALL              },
//    { 17,   RIL_CAPS_CALLCMD_RELEASECALL              },
//    { 18,   RIL_CAPS_CALLCMD_RELEASECALL              },
//    { 19,   RIL_CAPS_CALLCMD_RELEASECALL              },
//    { 21,   RIL_CAPS_CALLCMD_HOLDALLBUTONE            },
//    { 22,   RIL_CAPS_CALLCMD_HOLDALLBUTONE            },
//    { 23,   RIL_CAPS_CALLCMD_HOLDALLBUTONE            },
//    { 24,   RIL_CAPS_CALLCMD_HOLDALLBUTONE            },
//    { 25,   RIL_CAPS_CALLCMD_HOLDALLBUTONE            },
//    { 26,   RIL_CAPS_CALLCMD_HOLDALLBUTONE            },
//    { 27,   RIL_CAPS_CALLCMD_HOLDALLBUTONE            },
//    { 28,   RIL_CAPS_CALLCMD_HOLDALLBUTONE            },
//    { 29,   RIL_CAPS_CALLCMD_HOLDALLBUTONE            },
//};
//#define NUM_CAPSCALLCMDS    (sizeof(g_rglvmCapsCallCmds) / sizeof(LISTVALUEMAP))


//
// Bearer service connection speeds (first set)
//
static const LISTVALUEMAP g_rglvmCapsConnSpeeds1[] =
{
    { 0,    RIL_CAPS_SPEED1_AUTO        },
    { 1,    RIL_CAPS_SPEED1_300_V21     },
    { 2,    RIL_CAPS_SPEED1_1200_V22    },
    { 3,    RIL_CAPS_SPEED1_1200_75_V23 },
    { 4,    RIL_CAPS_SPEED1_2400_V22BIS },
    { 5,    RIL_CAPS_SPEED1_2400_V26TER },
    { 6,    RIL_CAPS_SPEED1_4800_V32    },
    { 7,    RIL_CAPS_SPEED1_9600_V32    },
    { 12,   RIL_CAPS_SPEED1_9600_V34    },
    { 14,   RIL_CAPS_SPEED1_14400_V34   },
    { 15,   RIL_CAPS_SPEED1_19200_V34   },
    { 16,   RIL_CAPS_SPEED1_28800_V34   },
    { 34,   RIL_CAPS_SPEED1_1200_V120   },
    { 36,   RIL_CAPS_SPEED1_2400_V120   },
    { 38,   RIL_CAPS_SPEED1_4800_V120   },
    { 39,   RIL_CAPS_SPEED1_9600_V120   },
    { 43,   RIL_CAPS_SPEED1_14400_V120  },
    { 47,   RIL_CAPS_SPEED1_19200_V120  },
    { 48,   RIL_CAPS_SPEED1_28800_V120  },
    { 49,   RIL_CAPS_SPEED1_38400_V120  },
    { 50,   RIL_CAPS_SPEED1_48000_V120  },
    { 65,   RIL_CAPS_SPEED1_300_V110    },
    { 66,   RIL_CAPS_SPEED1_1200_V110   },
    { 68,   RIL_CAPS_SPEED1_2400_V110   },
    { 70,   RIL_CAPS_SPEED1_4800_V110   },
    { 71,   RIL_CAPS_SPEED1_9600_V110   },
    { 75,   RIL_CAPS_SPEED1_14400_V110  },
    { 79,   RIL_CAPS_SPEED1_19200_V110  },
    { 80,   RIL_CAPS_SPEED1_28800_V110  },
    { 81,   RIL_CAPS_SPEED1_38400_V110  },
    { 82,   RIL_CAPS_SPEED1_48000_V110  },
    { 83,   RIL_CAPS_SPEED1_56000_V110  },
};
#define NUM_CAPSCONNSPEEDS1 (sizeof(g_rglvmCapsConnSpeeds1) / sizeof(LISTVALUEMAP))


//
// Bearer service connection speeds (second set)
//
static const LISTVALUEMAP g_rglvmCapsConnSpeeds2[] =
{
    { 51,   RIL_CAPS_SPEED2_56000_V120   },
    { 115,  RIL_CAPS_SPEED2_56000_TRANSP },
    { 116,  RIL_CAPS_SPEED2_64000_TRANSP },
#if defined(RIL_CAPS_SPEED2_32000_PIAFS32K)
    { 120,  RIL_CAPS_SPEED2_32000_PIAFS32K },
    { 121,  RIL_CAPS_SPEED2_64000_PIAFS64K },
    { 130,  RIL_CAPS_SPEED2_28800_MULTIMEDIA },
    { 131,  RIL_CAPS_SPEED2_32000_MULTIMEDIA },
    { 132,  RIL_CAPS_SPEED2_33600_MULTIMEDIA },
    { 133,  RIL_CAPS_SPEED2_56000_MULTIMEDIA },
    { 134,  RIL_CAPS_SPEED2_64000_MULTIMEDIA },
#endif
};
#define NUM_CAPSCONNSPEEDS2 (sizeof(g_rglvmCapsConnSpeeds2) / sizeof(LISTVALUEMAP))


//
// Bearer service names
//
static const LISTVALUEMAP g_rglvmCapsSvcNames[] =
{
    { 0,    RIL_CAPS_BSVCNAME_DATACIRCUIT_ASYNC_UDI_MODEM },
    { 1,    RIL_CAPS_BSVCNAME_DATACIRCUIT_SYNC_UDI_MODEM  },
    { 2,    RIL_CAPS_BSVCNAME_PADACCESS_ASYNC_UDI         },
    { 3,    RIL_CAPS_BSVCNAME_PACKETACCESS_SYNC_UDI       },
    { 4,    RIL_CAPS_BSVCNAME_DATACIRCUIT_ASYNC_RDI       },
    { 5,    RIL_CAPS_BSVCNAME_DATACIRCUIT_SYNC_RDI        },
    { 6,    RIL_CAPS_BSVCNAME_PADACCESS_ASYNC_RDI         },
    { 7,    RIL_CAPS_BSVCNAME_PACKETACCESS_SYNC_RDI       },
};
#define NUM_CAPSSVCNAMES    (sizeof(g_rglvmCapsSvcNames) / sizeof(LISTVALUEMAP))


//
// Bearer service connection elements
//
static const LISTVALUEMAP g_rglvmCapsConnElems[] =
{
    { 0,    RIL_CAPS_BSVCCE_TRANSPARENT         },
    { 1,    RIL_CAPS_BSVCCE_NONTRANSPARENT      },
    { 2,    RIL_CAPS_BSVCCE_BOTH_TRANSPARENT    },
    { 3,    RIL_CAPS_BSVCCE_BOTH_NONTRANSPARENT },
};
#define NUM_CAPSCONNELEMS   (sizeof(g_rglvmCapsConnElems) / sizeof(LISTVALUEMAP))


//
// Phonebook storage locations
//
static const LISTSTRINGMAP g_rglsmCapsPBLocations[] =
{
    { "EN",   RIL_CAPS_PBLOC_SIMEMERGENCY   },
    { "FD",   RIL_CAPS_PBLOC_SIMFIXDIALING  },
    { "LD",   RIL_CAPS_PBLOC_SIMLASTDIALING },
    { "ON",   RIL_CAPS_PBLOC_OWNNUMBERS     },
    { "SM",   RIL_CAPS_PBLOC_SIMPHONEBOOK   },
};
#define NUM_CAPSPBLOCS      (sizeof(g_rglsmCapsPBLocations) / sizeof(LISTSTRINGMAP))


//
// Messaging service types
//
static const LISTVALUEMAP g_rglvmCapsMsgSvcTypes[] =
{
    { 0,    RIL_CAPS_MSGSVCTYPE_PHASE2     },
    { 1,    RIL_CAPS_MSGSVCTYPE_PHASE2PLUS },
};
#define NUM_CAPSMSGSVCTYPES (sizeof(g_rglvmCapsMsgSvcTypes) / sizeof(LISTVALUEMAP))


//
// Message storage locations
//
static const LISTSTRINGMAP g_rglsmCapsMsgLocations[] =
{
    { "BM",   RIL_CAPS_MSGLOC_BROADCAST    },
    { "BR",   RIL_CAPS_MSGLOC_STATUSREPORT },
    { "MT",   RIL_CAPS_MSGLOC_SIM          },
};
#define NUM_CAPSMSGLOCS     (sizeof(g_rglsmCapsMsgLocations) / sizeof(LISTSTRINGMAP))


//
// Message status values
//
static const LISTVALUEMAP g_rglvmCapsMsgStatusValues[] =
{
    { 0,    RIL_CAPS_MSGSTATUS_RECUNREAD },
    { 1,    RIL_CAPS_MSGSTATUS_RECREAD   },
    { 2,    RIL_CAPS_MSGSTATUS_STOUNSENT },
    { 3,    RIL_CAPS_MSGSTATUS_STOSENT   },
};
#define NUM_CAPSMSGSTATUSVALUES (sizeof(g_rglvmCapsMsgStatusValues) / sizeof(LISTVALUEMAP))


//
// Locking facilities
//
static const LISTSTRINGMAP g_rglsmCapsLockFacilities[] =
{
    { "CS",   RIL_CAPS_LOCKFACILITY_CNTRL           },
    { "FD",   RIL_CAPS_LOCKFACILITY_SIM_FIXEDIALING },
    { "P2",   RIL_CAPS_LOCKFACILITY_SIM_PIN2        },
    { "PC",   RIL_CAPS_LOCKFACILITY_CORPPERS        },
    { "PF",   RIL_CAPS_LOCKFACILITY_PH_FSIM         },
    { "PN",   RIL_CAPS_LOCKFACILITY_NETWORKPERS     },
    { "PP",   RIL_CAPS_LOCKFACILITY_SERVICEPROVPERS },
    { "PS",   RIL_CAPS_LOCKFACILITY_PH_SIM          },
    { "PU",   RIL_CAPS_LOCKFACILITY_NETWORKSUBPERS  },
    { "SC",   RIL_CAPS_LOCKFACILITY_SIM             },
};
#define NUM_CAPSLOCKFACILITIES  (sizeof(g_rglsmCapsLockFacilities) / sizeof(LISTSTRINGMAP))


//
// Types of call barring
//
static const LISTSTRINGMAP g_rglsmCapsBarrTypes[] =
{
    { "AB",   RIL_CAPS_BARRTYPE_ALLBARRING          },
    { "AC",   RIL_CAPS_BARRTYPE_ALLINCOMINGBARRING  },
    { "AG",   RIL_CAPS_BARRTYPE_ALLOUTGOINGBARRING  },
    { "AI",   RIL_CAPS_BARRTYPE_ALLINCOMING         },
    { "AO",   RIL_CAPS_BARRTYPE_ALLOUTGOING         },
    { "IR",   RIL_CAPS_BARRTYPE_INCOMINGROAMING     },
    { "NS",   RIL_CAPS_BARRTYPE_INCOMINGNOTINSIM    },
    { "OI",   RIL_CAPS_BARRTYPE_OUTGOINGINT         },
    { "OX",   RIL_CAPS_BARRTYPE_OUTGOINGINTEXTOHOME },
};
#define NUM_CAPSBARRTYPES   (sizeof(g_rglsmCapsBarrTypes) / sizeof(LISTSTRINGMAP))

//
// Forwarding reasons
//
//static const LISTVALUEMAP g_rglvmCapsFwdReasons[] =
//{
//    { 0,    RIL_CAPS_FWDREASON_UNCONDITIONAL  },
//    { 1,    RIL_CAPS_FWDREASON_MOBILEBUSY     },
//    { 2,    RIL_CAPS_FWDREASON_NOREPLY        },
//    { 3,    RIL_CAPS_FWDREASON_UNREACHABLE    },
//    { 4,    RIL_CAPS_FWDREASON_ALLFORWARDING  },
//    { 5,    RIL_CAPS_FWDREASON_ALLCONDITIONAL },
//};
//#define NUM_CAPSFWDREASONS  (sizeof(g_rglvmCapsFwdReasons) / sizeof(LISTVALUEMAP))

//
//
//
HRESULT ParseFakeDWord(const DWORD dwValue, void*& pBlob, UINT& cbBlob)
{
    DWORD* pdw = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

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

    *pdw = dwValue;

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

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


//
//
//
static HRESULT ParseRangeRsp(const LPCSTR szCmd, const BOOL fIgnoreRest, LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    BOOL fParsedPostfix;
    RILRANGE* prr = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

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

    // Parse "<prefix><command><range><CR><LF>"
    if (!ParseRspPrefix(szRsp, szRsp)              ||
        !MatchStringBeginning(szRsp, szCmd, szRsp) ||
        !ParseRange(szRsp, *prr, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Parse "<postfix>" (skipping the rest of the response, if necessary)
    if (fIgnoreRest) {
        fParsedPostfix = FindRspPostfix(szRsp, szRsp);
    } else {
        fParsedPostfix = ParseRspPostfix(szRsp, szRsp);
    }
    if (!fParsedPostfix) {
        hr = E_FAIL;
        goto Error;
    }

    pBlob = (void*)prr;
    cbBlob = sizeof(RILRANGE);

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


//
//
//
static HRESULT ParseRangeListRsp(const LPCSTR szCmd, const LISTVALUEMAP* const rglvmMapArray, const UINT nMapArrayLength,
                                 const BOOL fIgnoreRest, LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    BOOL fParsedPostfix;
    DWORD* pdw = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

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

    // Parse "<prefix><command><list><CR><LF>"
    if (!ParseRspPrefix(szRsp, szRsp)              ||
        !MatchStringBeginning(szRsp, szCmd, szRsp) ||
        !ParseRangeList(szRsp, rglvmMapArray, nMapArrayLength, *pdw, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Parse "<postfix>" (skipping the rest of the response, if necessary)
    if (fIgnoreRest) {
        fParsedPostfix = FindRspPostfix(szRsp, szRsp);
    } else {
        fParsedPostfix = ParseRspPostfix(szRsp, szRsp);
    }
    if (!fParsedPostfix) {
        hr = E_FAIL;
        goto Error;
    }

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

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


//
//
//
static HRESULT ParseStringListRsp(const LPCSTR szCmd, const LISTSTRINGMAP* const rglsmMapArray, const UINT nMapArrayLength,
                                  LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    DWORD* pdw = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

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

    // Parse "<prefix><command><list><postfix>"
    if (!ParseRspPrefix(szRsp, szRsp)                                        ||
        !MatchStringBeginning(szRsp, szCmd, szRsp)                           ||
        !ParseStringList(szRsp, rglsmMapArray, nMapArrayLength, *pdw, szRsp) ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

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

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


//
//
//
static HRESULT ParseGetDevCapsDial(LPCSTR /*szRsp*/, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    RILCAPSDIAL* prcd = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

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

	//ELSx variants don't support voice call.
	if(SUPPORTS_ONLY_DATAMODE){
    	prcd->dwCallTypes = RIL_CAPS_CALLTYPE_DATA | RIL_CAPS_CALLTYPE_FAX;
	}else{
		prcd->dwCallTypes = RIL_CAPS_CALLTYPE_VOICE | RIL_CAPS_CALLTYPE_DATA | RIL_CAPS_CALLTYPE_FAX;
	}
    prcd->dwOptions = RIL_CAPS_DIALOPT_RESTRICTID | RIL_CAPS_DIALOPT_PRESENTID | RIL_CAPS_DIALOPT_CLOSEDGROUP;

    prcd->dwParams = RIL_PARAM_CD_CALLTYPES | RIL_PARAM_CD_OPTIONS;

    pBlob = (void*)prcd;
    cbBlob = sizeof(RILCAPSDIAL);

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


//
//
//
static HRESULT ParseGetDevCapsDTMFDurationRange(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    return ParseRangeRsp("+VTS: (0-9,#,*,A-D),", FALSE, szRsp, pBlob, cbBlob);
}


//
//
//
static HRESULT ParseGetDevCapsCallMgtCmds(LPCSTR /*szRsp*/, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    // The AT+CHLD=4 and AT+CHLD=5 functions are implemented as ATD options in
    // Cinterion modules. So cannot use AT+CHLD=? to get the capabilities.
    HRESULT hr = ParseFakeDWord(RIL_CAPS_CALLCMD_RELEASEHELD |
                                RIL_CAPS_CALLCMD_RELEASEACTIVE_ACCEPTHELD |
                                RIL_CAPS_CALLCMD_HOLDACTIVE_ACCEPTHELD |
                                RIL_CAPS_CALLCMD_ADDHELDTOCONF |
                                RIL_CAPS_CALLCMD_ADDHELDTOCONF_DISCONNECT |
                                RIL_CAPS_CALLCMD_RELEASECALL |
                                RIL_CAPS_CALLCMD_HOLDALLBUTONE, pBlob, cbBlob);

    return hr;
}


//
//
//
static HRESULT ParseGetDevCapsBearerSvc(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    RILCAPSBEARERSVC* prcbs = NULL;
    LPCSTR szDummy;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

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

    // Parse "<prefix>+CBST: <speed_list>"
    if (!ParseRspPrefix(szRsp, szRsp)                                                                  ||
        !MatchStringBeginning(szRsp, "+CBST: ", szRsp)                                                 ||
        !ParseRangeList(szRsp, g_rglvmCapsConnSpeeds1, NUM_CAPSCONNSPEEDS1, prcbs->dwSpeeds1, szDummy) ||
        !ParseRangeList(szRsp, g_rglvmCapsConnSpeeds2, NUM_CAPSCONNSPEEDS2, prcbs->dwSpeeds2, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    prcbs->dwParams |= (RIL_PARAM_CBS_SPEEDS1 | RIL_PARAM_CBS_SPEEDS2);

    // Parse ",<svcname_list>"
    if (!MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseRangeList(szRsp, g_rglvmCapsSvcNames, NUM_CAPSSVCNAMES, prcbs->dwServiceNames, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    prcbs->dwParams |= RIL_PARAM_CBS_SERVICENAMES;

    // Parse ",<connelem_list><postfix>"
    if (!MatchStringBeginning(szRsp, ",", szRsp)                                                            ||
        !ParseRangeList(szRsp, g_rglvmCapsConnElems, NUM_CAPSCONNELEMS, prcbs->dwConnectionElements, szRsp) ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    prcbs->dwParams |= RIL_PARAM_CBS_CONNECTIONELEMENTS;

    pBlob = (void*)prcbs;
    cbBlob = sizeof(RILCAPSBEARERSVC);

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


//
//
//
static HRESULT ParseGetDevCapsRLP(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    UINT nValue;
    UINT nUsed = 0;
    UINT nAllocated = 0;
    RILCAPSRLP* rgrcrlp = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

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

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

        memset(&rgrcrlp[nUsed], 0x00, sizeof(RILCAPSRLP));
        rgrcrlp[nUsed].cbSize = sizeof(RILCAPSRLP);

        // Parse "<iws_range>"
        if (!ParseRange(szRsp, rgrcrlp[nUsed].rrIWSRange, szRsp)) {
            goto Continue;
        }
        rgrcrlp[nUsed].dwParams |= RIL_PARAM_CRLP_IWSRANGE;

        // Parse ",<mws_range>"
        if (!MatchStringBeginning(szRsp, ",", szRsp) ||
            !ParseRange(szRsp, rgrcrlp[nUsed].rrMWSRange, szRsp)) {
            goto Continue;
        }
        rgrcrlp[nUsed].dwParams |= RIL_PARAM_CRLP_MWSRANGE;

        // Parse ",<acktimer_range>"
        if (!MatchStringBeginning(szRsp, ",", szRsp) ||
            !ParseRange(szRsp, rgrcrlp[nUsed].rrAckTimerRange, szRsp)) {
            goto Continue;
        }
        rgrcrlp[nUsed].dwParams |= RIL_PARAM_CRLP_ACKTIMERRANGE;

        // Parse ",<retransmissionatts_range>"
        if (!MatchStringBeginning(szRsp, ",", szRsp) ||
            !ParseRange(szRsp, rgrcrlp[nUsed].rrRetransmissionAttsRange, szRsp)) {
            goto Continue;
        }
        rgrcrlp[nUsed].dwParams |= RIL_PARAM_CRLP_RETRANSMISSIONATTSRANGE;

        // Parse ","
        if (MatchStringBeginning(szRsp, ",", szRsp)) {

            if (*szRsp=='(') szRsp++;    // Ignore '(' and ')' characters around version

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

            if (*szRsp==')') szRsp++;    // Ignore '(' and ')' characters around version

            rgrcrlp[nUsed].dwVersion = nValue;
            rgrcrlp[nUsed].dwParams |= RIL_PARAM_CRLP_VERSION;

            // Parse ","
            if ((nValue>1) && (MatchStringBeginning(szRsp, ",", szRsp))) {
                // Parse "<reseqperiod_range>"
                if (!ParseRange(szRsp, rgrcrlp[nUsed].rrReseqPeriodRange, szRsp)) {
                    goto Continue;
                }
                rgrcrlp[nUsed].dwParams |= RIL_PARAM_CRLP_RESEQPERIODRANGE;
            }
        }

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

        // Increment the array index
        nUsed++;

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

    pBlob = (void*)rgrcrlp;
    cbBlob = nUsed * sizeof(RILCAPSRLP);

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


//
//
//
static HRESULT ParseGetDevCapsEquipmentStates(LPCSTR /*szRsp*/, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    DWORD dwEquipmentStates;

    dwEquipmentStates = RIL_CAPS_EQSTATE_MINIMUM|RIL_CAPS_EQSTATE_FULL;
    dwEquipmentStates |= RIL_CAPS_EQSTATE_DISABLETXANDRX;
    return ParseFakeDWord(dwEquipmentStates, pBlob, cbBlob);
}


//
//
//
static HRESULT ParseGetDevCapsPBStoreLocs(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    return ParseStringListRsp("+CPBS: ", g_rglsmCapsPBLocations, NUM_CAPSPBLOCS, szRsp, pBlob, cbBlob);
}

//
// Used for modules without phonebook support, returns a zero,
// indicating, that ther are no phonebook locations.
//
static HRESULT ParseGetDevCapsPBStoreLocsNoop(LPCSTR /*szRsp*/, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    DWORD* pdw = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

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

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

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

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

    // If there is no response string, the radio is currently off.
    if (szRsp == NULL) {
        hr = RIL_E_RADIOOFF;
        goto Error;
    }

    if (pBlob != NULL)
        (void)FreeBlob(pBlob);
    pBlob = NULL;
    cbBlob = 0;

    // Instead of returning an error, the RIL generates an empty answer
    pdw = (DWORD*)AllocBlob(sizeof(DWORD));
    if (!pdw) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(pdw, 0x00, sizeof(DWORD));

    pBlob = (void*)pdw;
    cbBlob = sizeof(DWORD);
    dwCode = RIL_RESULT_OK;
Error:
    return hr;
}


//
//
//
static HRESULT ParseGetDevCapsPBIndexRange(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    return ParseRangeRsp("+CPBR: ", TRUE, szRsp, pBlob, cbBlob);
}

//
//
//
static HRESULT ParseGetDevCapsPBIndexRangeCRSM(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    RILRANGE* prr = NULL;
    HRESULT hr = S_OK;
    DWORD dwNumbers = 0;

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

    pBlob = NULL;
    cbBlob = 0;

    if (SUCCEEDED(hr) && dwNumbers > 0) {
        prr = (RILRANGE*)AllocBlob(sizeof(RILRANGE));
        if (!prr) {
            hr = E_OUTOFMEMORY;
            goto Error;
        }
        memset(prr, 0x00, sizeof(RILRANGE));

        prr->dwMinValue = 1;
        prr->dwMaxValue = dwNumbers & 0xFFFF;

        pBlob = (void*)prr;
        cbBlob = sizeof(RILRANGE);
    }
    else if (SUCCEEDED(hr) && dwNumbers > 0) {
        hr = RIL_E_INVALIDINDEX;
    }

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

//
// Used for modules without phonebook support, returns an valid,
// but emtpy, RILRANGE structure.
//
static HRESULT ParseGetDevCapsPBIndexRangeNoop(LPCSTR /*szRsp*/, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    RILRANGE* prr = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

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

    pBlob = (void*)prr;
    cbBlob = sizeof(RILRANGE);

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

//
//
//
static HRESULT ParseGetDevCapsPBEntryLength(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    RILCAPSPBENTRYLENGTH* prcpbel = NULL;
    UINT nValue;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

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

    // Parse "<prefix>+CPBR: "
    if (!ParseRspPrefix(szRsp, szRsp)                  ||
        !MatchStringBeginning(szRsp, "+CPBR: ", szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    // Parse "(1-"
    if (!MatchStringBeginning(szRsp, "(1-", szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    // Parse "<num entries>", not a required value
    if (!ParseUInt(szRsp, TRUE, nValue, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    // Parse ","
    if (!MatchStringBeginning(szRsp, "),", szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    // Parse "<num_length>"
    if (ParseUInt(szRsp, TRUE, nValue, szRsp)) {
        prcpbel->dwMaxAddressLength = nValue;
        prcpbel->dwParams |= RIL_PARAM_CPBEL_MAXADDRESSLENGTH;
    }
    // Parse ","
    if (!MatchStringBeginning(szRsp, ",", szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    // Parse "<text_length>"
    if (ParseUInt(szRsp, TRUE, nValue, szRsp)) {
        prcpbel->dwMaxTextLength = nValue;
        prcpbel->dwParams |= RIL_PARAM_CPBEL_MAXTEXTLENGTH;
    }

    // Parse <postfix> (there might be additional parameters, but they aren't relevant here)
    if (!FindRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    pBlob = (void*)prcpbel;
    cbBlob = sizeof(RILCAPSPBENTRYLENGTH);

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

//
//
//
static HRESULT ParseGetDevCapsPBEntryLengthCRSM(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    RILCAPSPBENTRYLENGTH* prcpbel = NULL;
    HRESULT hr = S_OK;
    DWORD dwNumbers = 0;

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

    pBlob = NULL;
    cbBlob = 0;

    if (SUCCEEDED(hr) && dwNumbers > 0) {
        prcpbel = (RILCAPSPBENTRYLENGTH*)AllocBlob(sizeof(RILCAPSPBENTRYLENGTH));
        if (!prcpbel) {
            hr = E_OUTOFMEMORY;
            goto Error;
        }
        memset(prcpbel, 0x00, sizeof(RILCAPSPBENTRYLENGTH));
        prcpbel->cbSize = sizeof(RILCAPSPBENTRYLENGTH);

        prcpbel->dwMaxAddressLength = 20;
        prcpbel->dwMaxTextLength = (dwNumbers >> 16) - 14;
        prcpbel->dwParams = RIL_PARAM_CPBEL_MAXADDRESSLENGTH|RIL_PARAM_CPBEL_MAXTEXTLENGTH;

        pBlob = (void*)prcpbel;
        cbBlob = sizeof(RILCAPSPBENTRYLENGTH);
    }
    else if (SUCCEEDED(hr) && dwNumbers > 0) {
        hr = RIL_E_INVALIDINDEX;
    }

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

//
// Used for modules without phonebook support, returns an valid,
// but emtpy, RILCAPSPBENTRYLENGTH structure.
//
static HRESULT ParseGetDevCapsPBEntryLengthNoop(LPCSTR /*szRsp*/, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    RILCAPSPBENTRYLENGTH* prcpbel = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

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

    pBlob = (void*)prcpbel;
    cbBlob = sizeof(RILCAPSPBENTRYLENGTH);

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

//
//
//
static HRESULT ParseGetDevCapsMsgSvcTypes(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    return ParseRangeListRsp("+CSMS: ", g_rglvmCapsMsgSvcTypes, NUM_CAPSMSGSVCTYPES, FALSE, szRsp, pBlob, cbBlob);
}


//
//
//
static HRESULT ParseGetDevCapsMsgMemoryLocs(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    RILCAPSMSGMEMORYLOCATIONS* prcmml = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

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

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

    if (!MatchStringBeginning(szRsp, "+CPMS: ", szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }

    // Skip extraneous '(' at beginning if it's there
    if ((szRsp[0]=='(') && (szRsp[1]=='('))
    {
        szRsp++;
    }

    if (!ParseStringList(szRsp, g_rglsmCapsMsgLocations, NUM_CAPSMSGLOCS, prcmml->dwReadLocations, szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }

    prcmml->dwParams |= RIL_PARAM_CMML_READLOCATIONS;

    // Parse ","
    if (MatchStringBeginning(szRsp, ",", szRsp)) {
        // Parse "<writeloc_list>"
        if (!ParseStringList(szRsp, g_rglsmCapsMsgLocations, NUM_CAPSMSGLOCS, prcmml->dwWriteLocations, szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        prcmml->dwParams |= RIL_PARAM_CMML_WRITELOCATIONS;

        // Parse ","
        if (MatchStringBeginning(szRsp, ",", szRsp)) {
            // Parse "<storeloc_list>"
            if (!ParseStringList(szRsp, g_rglsmCapsMsgLocations, NUM_CAPSMSGLOCS, prcmml->dwStoreLocations, szRsp)) {
                hr = E_FAIL;
                goto Error;
            }
            prcmml->dwParams |= RIL_PARAM_CMML_STORELOCATIONS;
        }
    }

    pBlob = (void*)prcmml;
    cbBlob = sizeof(RILCAPSMSGMEMORYLOCATIONS);

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


static HRESULT ParseGetDevCapsMsgMemoryLocsNoop(LPCSTR /*szRsp*/, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    RILCAPSMSGMEMORYLOCATIONS* prcmml = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

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

    prcmml->dwReadLocations = RIL_CAPS_MSGLOC_SIM;
    prcmml->dwParams |= RIL_PARAM_CMML_READLOCATIONS;

    prcmml->dwWriteLocations = RIL_CAPS_MSGLOC_SIM;
    prcmml->dwParams |= RIL_PARAM_CMML_WRITELOCATIONS;

    prcmml->dwStoreLocations = RIL_CAPS_MSGLOC_SIM;
    prcmml->dwParams |= RIL_PARAM_CMML_STORELOCATIONS;

    pBlob = (void*)prcmml;
    cbBlob = sizeof(RILCAPSMSGMEMORYLOCATIONS);

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

//
//
//
static HRESULT ParseGetDevCapsBroadcastMsgLangs(LPCSTR /*szRsp*/, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    DWORD dwLanguages = (RIL_CAPS_DCSLANG_GERMAN | RIL_CAPS_DCSLANG_ENGLISH | RIL_CAPS_DCSLANG_ITALIAN |
                         RIL_CAPS_DCSLANG_FRENCH | RIL_CAPS_DCSLANG_SPANISH | RIL_CAPS_DCSLANG_DUTCH | RIL_CAPS_DCSLANG_SWEDISH |
                         RIL_CAPS_DCSLANG_DANISH | RIL_CAPS_DCSLANG_PORTUGUESE | RIL_CAPS_DCSLANG_FINNISH |
                         RIL_CAPS_DCSLANG_NORWEGIAN | RIL_CAPS_DCSLANG_GREEK | RIL_CAPS_DCSLANG_TURKISH |
                         RIL_CAPS_DCSLANG_HUNGARIAN | RIL_CAPS_DCSLANG_POLISH | RIL_CAPS_DCSLANG_CZECH);

    return ParseFakeDWord(dwLanguages, pBlob, cbBlob);
}


//
//
//
static HRESULT ParseGetDevCapsMsgConfigIndexRange(LPCSTR /*szRsp*/, void*& /*pBlob*/, UINT& /*cbBlob*/, LPVOID /*pParam*/)
{
    return E_NOTIMPL;
}


//
//
//
static HRESULT ParseGetDevCapsMsgStatusValues(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    return ParseRangeListRsp("+CMGL: ", g_rglvmCapsMsgStatusValues, NUM_CAPSMSGSTATUSVALUES, FALSE, szRsp, pBlob, cbBlob);
}


//
//
//
static HRESULT ParseGetDevCapsPrefOpIndexRange(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    HRESULT hr;

    hr = ParseRangeRsp("+CPOL: ", TRUE, szRsp, pBlob, cbBlob);

#ifdef RIL_WM6_ACT_SELECTION
    if (SUCCEEDED(hr) && pBlob != NULL) {
        RILRANGE *prr;

        prr = (RILRANGE *)pBlob;
        prr->dwMaxValue *= 2;
    }
#endif
    return hr;
}

static HRESULT ParseGetDevCapsPrefOpIndexRangeError(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/, DWORD& /*dwCode*/)
{
    // If there is no response string, the radio is currently off.
    if (szRsp == NULL)
        return RIL_E_RADIOOFF;

    // This SIM doesn't allow access to the operator list, but since the Microsoft MMI interprets
    // the error code from the radio wrong, just give some default value.
    return ParseRangeRsp("+CPOL: ", TRUE, "+CPOL: (1-1)", pBlob, cbBlob);
}


//
//
//
static HRESULT ParseGetDevCapsLockFacilities(LPCSTR /*szRsp*/, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    const char szDefaultLockFacilities[] = "+CLCK: (\"AB\",\"AC\",\"AG\",\"AI\",\"AO\",\"IR\",\"OI\",\"OX\",\"SC\",\"PN\",\"PU\",\"PP\",\"PC\",\"PF\",\"PS\",\"P2\",\"FD\")\r\n";
    return ParseStringListRsp("+CLCK: ", g_rglsmCapsLockFacilities, NUM_CAPSLOCKFACILITIES, szDefaultLockFacilities, pBlob, cbBlob);
}


//
//
//
static HRESULT ParseGetDevCapsLockPwdLengths(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    char szFacility[MAX_ATCMD_LEN];
    UINT nValue;
    UINT i;
    UINT nUsed = 0;
    UINT nAllocated = 0;
    RILCAPSLOCKINGPWDLENGTH* rgrclpl = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

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

    // Parse "("
    for (;;) {
        if (nUsed == nAllocated) {
            if (!AllocateOrReallocateStorage((BYTE**)&rgrclpl, sizeof(RILCAPSLOCKINGPWDLENGTH), nUsed, &nAllocated, CAPS_ALLOC_NUMBER)) {
                hr = E_OUTOFMEMORY;
                goto Error;
            }
        }

        memset(&rgrclpl[nUsed], 0x00, sizeof(RILCAPSLOCKINGPWDLENGTH));
        rgrclpl[nUsed].cbSize = sizeof(RILCAPSLOCKINGPWDLENGTH);

        // Parse "(<facility>,<pwd_length>)"
        if (!MatchStringBeginning(szRsp, "(", szRsp)         ||
            !ParseString(szRsp, szFacility, MAX_ATCMD_LEN, szRsp) ||
            !MatchStringBeginning(szRsp, ",", szRsp)         ||
            !ParseUInt(szRsp, TRUE, nValue, szRsp)           ||
            !MatchStringBeginning(szRsp, ")", szRsp)) {
            goto Continue;
        }

        for (i = 0; i < NUM_LOCKFACILITIES; i++) {
            if (!strcmp(szFacility, g_rgszLockFacilities[i])) {
                rgrclpl[nUsed].dwFacility = i;
                rgrclpl[nUsed].dwPasswordLength = nValue;
                rgrclpl[nUsed].dwParams |= (RIL_PARAM_CLPL_FACILITY | RIL_PARAM_CLPL_PASSWORDLENGTH);
                break;
            }
        }
        if (NUM_LOCKFACILITIES == i) {
            // We couldn't match the response with anything
            goto Continue;
        }

        // Increment the array index
        nUsed++;

Continue:
        // Parse ","
        if (!MatchStringAnywhere(szRsp, ",", szRsp)) {
            break;
        }
    }

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

    pBlob = (void*)rgrclpl;
    cbBlob = nUsed * sizeof(RILCAPSLOCKINGPWDLENGTH);

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


//
//
//
static HRESULT ParseGetDevCapsBarrTypes(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    return ParseStringListRsp("+CLCK: ", g_rglsmCapsBarrTypes, NUM_CAPSBARRTYPES, szRsp, pBlob, cbBlob);
}


//
//
//
static HRESULT ParseGetDevCapsBarrPwdLengths(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    char szType[MAX_ATCMD_LEN];
    UINT nValue;
    UINT i;
    UINT nUsed = 0;
    UINT nAllocated = 0;
    RILCAPSBARRINGPWDLENGTH* rgrcbpl = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

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

    // Parse "("
    for (;;) {
        if (nUsed == nAllocated) {
            if (!AllocateOrReallocateStorage((BYTE**)&rgrcbpl, sizeof(RILCAPSBARRINGPWDLENGTH), nUsed, &nAllocated, CAPS_ALLOC_NUMBER)) {
                hr = E_OUTOFMEMORY;
                goto Error;
            }
        }

        memset(&rgrcbpl[nUsed], 0x00, sizeof(RILCAPSBARRINGPWDLENGTH));
        rgrcbpl[nUsed].cbSize = sizeof(RILCAPSBARRINGPWDLENGTH);

        // Parse "(<facility>,<pwd_length>)"
        if (!MatchStringBeginning(szRsp, "(", szRsp)     ||
            !ParseString(szRsp, szType, MAX_ATCMD_LEN, szRsp) ||
            !MatchStringBeginning(szRsp, ",", szRsp)     ||
            !ParseUInt(szRsp, TRUE, nValue, szRsp)       ||
            !MatchStringBeginning(szRsp, ")", szRsp)) {
            goto Continue;
        }

        for (i = 0; i < NUM_BARRINGTYPES; i++) {
            if (!strcmp(szType, g_rgszBarringTypes[i])) {
                rgrcbpl[nUsed].dwType = i;
                rgrcbpl[nUsed].dwPasswordLength = nValue;
                rgrcbpl[nUsed].dwParams |= (RIL_PARAM_CBPL_TYPE | RIL_PARAM_CBPL_PASSWORDLENGTH);
                break;
            }
        }
        if (NUM_BARRINGTYPES == i) {
            // We couldn't match the response with anything
            goto Continue;
        }

        // Increment the array index
        nUsed++;

Continue:
        // Parse ","
        if (!MatchStringAnywhere(szRsp, ",", szRsp)) {
            break;
        }
    }

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

    pBlob = (void*)rgrcbpl;
    cbBlob = nUsed * sizeof(RILCAPSBARRINGPWDLENGTH);

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


//
//
//
static HRESULT ParseGetDevCapsFwdReasons(LPCSTR /*szRsp*/, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    return ParseFakeDWord(RIL_CAPS_FWDREASON_UNCONDITIONAL|RIL_CAPS_FWDREASON_MOBILEBUSY|RIL_CAPS_FWDREASON_NOREPLY|RIL_CAPS_FWDREASON_UNREACHABLE|RIL_CAPS_FWDREASON_ALLFORWARDING|RIL_CAPS_FWDREASON_ALLCONDITIONAL, pBlob, cbBlob);
}


//
//
//
static HRESULT ParseGetDevCapsInfoClasses(LPCSTR /*szRsp*/, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    return ParseFakeDWord(RIL_CAPS_INFOCLASS_VOICE | RIL_CAPS_INFOCLASS_DATA | RIL_CAPS_INFOCLASS_FAX, pBlob, cbBlob);
}


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

    pBlob = NULL;
    cbBlob = 0;

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

#ifdef RIL_USE_CSQ_NOTIFICATIONS
    *pdwSignalQualityImplCaps = RIL_CAPS_SIGNALQUALITY_NOTIFICATION;
#else
    *pdwSignalQualityImplCaps = RIL_CAPS_SIGNALQUALITY_POLLING;
#endif

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

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

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

    pBlob = NULL;
    cbBlob = 0;

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

    // GSM devices have SIM support.
    *pdwSimSupportCaps = RIL_CAPS_SIM_BASIC;

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

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

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

    pBlob = NULL;
    cbBlob = 0;

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

#ifdef RILDRV_CE6
    // Under Windows CE 6 the RIL must use notifications for indicating
    // the current call progress state.
    *pdwCallProgressNotification = true;
#else
    // The Cinterion modules support progress notifications using the AT^SLCC command.
    // But the RIL handles the ^SLCC notification internally and doesn't report
    // them to the OS, so we have to return false here.
    *pdwCallProgressNotification = false;
#endif

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

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

    return hr;
}

#ifdef RIL_NOTIFY_NITZ
static HRESULT ParseGetNITZSupport(LPCSTR /*szRsp*/, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    HRESULT hr = S_OK;
    DWORD * pdwNITZNotificationSetting = NULL;


    pBlob = NULL;
    cbBlob = 0;

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

    *pdwNITZNotificationSetting = RIL_CAPS_NITZ_ENABLED;

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

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

#ifdef RIL_CAPSTYPE_PHONECAPABILITY
static HRESULT ParseGetPhoneCapability(LPCSTR /*szRsp*/, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    HRESULT hr = S_OK;
    DWORD *pdwPhoneCaps = NULL;
    DWORD  dwPhoneCaps = 0;

    pBlob = NULL;
    cbBlob = 0;

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

    if (!GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("DefaultPhoneCapabilities"), &dwPhoneCaps)) {
        if (CDMA_ONLY_RADIO)
            dwPhoneCaps = RIL_CAPS_PHONECAPABILITY_CDMAHDR;
        else if (GLOBAL_MODE_RADIO)
            dwPhoneCaps = RIL_CAPS_PHONECAPABILITY_UMTS_GSM_CDMAHDR;
        else
            dwPhoneCaps = RIL_CAPS_PHONECAPABILITY_UMTS_GSM;
        // Windows Mobule currently doesn't have phone capability setting for LTE.
        // If it will ever get introduced, it will probably get the name
        // RIL_CAPS_PHONECAPABILITY_LTE.
#ifdef RIL_CAPS_PHONECAPABILITY_LTE
        dwPhoneCaps |= RIL_CAPS_PHONECAPABILITY_LTE;
#endif
    }

    *pdwPhoneCaps = dwPhoneCaps;

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

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

#ifdef RIL_CAPSTYPE_NOTIFICATIONIMPLEMENTATION
//
//
//
static HRESULT ParseGetNotificationImplemenation(LPCSTR /*szRsp*/, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    HRESULT hr = S_OK;
    DWORD * pdwNotificationImplCaps = 0;

    pBlob = NULL;
    cbBlob = 0;

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

#ifdef RILDRV_CE7
    // These notifications are supported by the RIL.
    *pdwNotificationImplCaps = RIL_CAPS_NOTIFICATION_SIGNALQUALITY |
                               RIL_CAPS_NOTIFICATION_EQUIPMENTSTATE |
                               RIL_CAPS_NOTIFICATION_GPRSREGISTRATIONSTATUS |
                               RIL_CAPS_NOTIFICATION_REGISTRATIONSTATUS;
#else
    // CE6 Radiometric test wants only a "1" indicating, that the RIL does support notifications.
    *pdwNotificationImplCaps = 1;
#endif

    // The Microsoft RIL allows to overwrite the capabilities with registry.
    (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, RIL_CAPS_NOTIFICATION_KEY, RIL_CAPS_NOTIFICATION_IMPL_PARAMS, pdwNotificationImplCaps);

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

Error:
    if (FAILED(hr)) {
        FreeBlob(pdwNotificationImplCaps);
    }

    return hr;
}
#endif

#ifdef RIL_CAPSTYPE_CALLSUPPORT
//
//
//
static HRESULT ParseGetCallSupportImplemenation(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    HRESULT hr = S_OK;
    DWORD *pdwCallSupportCaps = NULL;

    pBlob = NULL;
    cbBlob = 0;

    pdwCallSupportCaps = (DWORD *)AllocBlob(sizeof(DWORD));
    if (!pdwCallSupportCaps) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
	
	if(SUPPORTS_ONLY_DATAMODE){
		*pdwCallSupportCaps = RIL_CAPS_CALLSUPPORT_USSD;		
	}else{		
		*pdwCallSupportCaps = RIL_CAPS_CALLSUPPORT_BARRING |
								  RIL_CAPS_CALLSUPPORT_CALLID |
								  RIL_CAPS_CALLSUPPORT_CUG |
								  RIL_CAPS_CALLSUPPORT_FORWARD |
								  RIL_CAPS_CALLSUPPORT_HOLD |
								  RIL_CAPS_CALLSUPPORT_MPTY |
								  RIL_CAPS_CALLSUPPORT_REJECTWAITINGCALL |
								  RIL_CAPS_CALLSUPPORT_USSD |
								  RIL_CAPS_CALLSUPPORT_WAIT;
	}
    
    pBlob = (void*)pdwCallSupportCaps;
    cbBlob = sizeof(DWORD);

Error:
    if (FAILED(hr)) {
        FreeBlob(pdwCallSupportCaps);
    }

    return hr;
}
#endif

#ifdef RIL_CAPSTYPE_SMSSUPPORT
//
//
//
static HRESULT ParseGetSMSSupportImplemenation(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    HRESULT hr = S_OK;
    DWORD *pdwSMSSupportCaps = NULL;

    pBlob = NULL;
    cbBlob = 0;

    pdwSMSSupportCaps = (DWORD *)AllocBlob(sizeof(DWORD));
    if (!pdwSMSSupportCaps) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    *pdwSMSSupportCaps = RIL_CAPS_SMSSUPPORT_MOSMS |
                         RIL_CAPS_SMSSUPPORT_PERSISTLINK |
                         RIL_CAPS_CALLSUPPORT_WAIT;


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

Error:
    if (FAILED(hr)) {
        FreeBlob(pdwSMSSupportCaps);
    }

    return hr;
}
#endif

#ifdef RIL_CAPSTYPE_MSGINDEXRANGE
//
//
//
static HRESULT ParseMsgIndexRange(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    RILRANGE* prr = NULL;
    HRESULT hr = S_OK;
    UINT nUsed = 0;
    UINT nTotal = 0;
    char szTemp[MAX_ATCMD_LEN];

    pBlob = NULL;
    cbBlob = 0;

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

    // Parse "+CPMS: <mem1>,<used1>,<total1>,...<CR><LF>"
    if (!ParseRspPrefix(szRsp, szRsp)              ||
        !MatchStringBeginning(szRsp, "+CPMS: ", szRsp) ||
        !ParseString(szRsp, szTemp, MAX_ATCMD_LEN, szRsp) ||
        !MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseUInt(szRsp, TRUE, nUsed, szRsp) ||
        !MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseUInt(szRsp, TRUE, nTotal, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // If there is no SMS message store available, return error.
    if (nTotal == 0) {
        hr = RIL_E_NOTFOUND;
        goto Error;
    }

#ifdef NO_SMS_INDEX0
    // Even though the module may uses the range 0..<x-1>, the
    // RIL operates with the range 1..<x>.
    prr->dwMinValue = 1;
    prr->dwMaxValue = nTotal;
#else
    if (SMS_INDEX_STARTS_WITH_1) {
        prr->dwMinValue = 1;
        prr->dwMaxValue = nTotal;
    }
    else {
        prr->dwMinValue = 0;
        prr->dwMaxValue = nTotal - 1;
    }
#endif

    if (!FindRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    pBlob = (void*)prr;
    cbBlob = sizeof(RILRANGE);

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

//
//
//
HRESULT RILDrv_GetDevCaps(DWORD dwParam, DWORD dwCapsType)
{
    HRESULT hr = S_OK;
    LPCSTR szCmd = NULL;
    char szCRSMCmd[MAX_ATCMD_LEN];
    DWORD dwOptions = CMDOPT_NONE;
    PFN_CMD_PARSE pfnParse = NULL;
    PFN_CMD_ERROR pfnParseError = NULL;
    BOOL fDataPort = FALSE;
    BOOL bDoRetry = FALSE;
    BOOL bSendFollowingAT = FALSE;
    PVOID pParam = NULL;
    HCCMD_MODE nCmdMode = HCCMD_STANDARD;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: called RILDrv_DevCaps(0x%x)\r\n"), dwCapsType));
    switch (dwCapsType)
    {
        case RIL_CAPSTYPE_DIAL:
            szCmd = NULL;
            dwOptions = CMDOPT_NOOP;
            pfnParse = ParseGetDevCapsDial;
            break;

        case RIL_CAPSTYPE_DTMFDURATIONRANGE:
			if(UNSUPPORTS_AUDIO){
				hr = E_NOTIMPL;
			}else{
				szCmd = "AT+VTS=?\r";
            	pfnParse = ParseGetDevCapsDTMFDurationRange;
			}
            break;

        case RIL_CAPSTYPE_CALLMGTCMDS:
			if(SUPPORTS_ONLY_DATAMODE){
				hr = E_NOTIMPL;
			}else{
	            szCmd = NULL;
	            dwOptions = CMDOPT_NOOP;
	            pfnParse = ParseGetDevCapsCallMgtCmds;
			}
            break;

        case RIL_CAPSTYPE_BEARERSERVICE:
			if(SUPPORTS_ONLY_DATAMODE){
				hr = E_NOTIMPL;
			}else{
	            nCmdMode = HCCMD_DATAPORT_ONLY;
	            szCmd = "AT+CBST=?\r";
	            pfnParse = ParseGetDevCapsBearerSvc;
	            dwOptions = CMDOPT_GENERATE_ERROR_CODE;
	            fDataPort = TRUE;
			}
            break;

        case RIL_CAPSTYPE_RLP:
			if(SUPPORTS_ONLY_DATAMODE){
				hr = E_NOTIMPL;
			}else{
	            nCmdMode = HCCMD_DATAPORT_ONLY;
	            szCmd = "AT+CRLP=?\r";
	            pfnParse = ParseGetDevCapsRLP;
	            dwOptions = CMDOPT_GENERATE_ERROR_CODE;
	            fDataPort = TRUE;
			}
            break;

        case RIL_CAPSTYPE_EQUIPMENTSTATES:
            szCmd = NULL;
            dwOptions = CMDOPT_NOOP;
            pfnParse = ParseGetDevCapsEquipmentStates;
            break;

        case RIL_CAPSTYPE_PBSTORELOCATIONS:
            if (pHandle->GetDevice()->PhoneBookAccessDisabled()) {
                szCmd = NULL;
                dwOptions = CMDOPT_NOOP;
                pfnParse = ParseGetDevCapsPBStoreLocsNoop;
            }
            else {
                szCmd = "AT+CPBS=?\r";
                pfnParse = ParseGetDevCapsPBStoreLocs;
                pfnParseError = ParseGetDevCapsPBStoreLocsError;
                dwOptions = CMDOPT_GENERATE_ERROR_CODE;
                bDoRetry = TRUE;
            }
            break;

        case RIL_CAPSTYPE_PBINDEXRANGE:
			RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: GetCurrentSIMType = %d\r\n"), (int)pHandle->GetDevice()->GetCurrentSIMType()));
			
            if (pHandle->GetDevice()->PhoneBookAccessDisabled()) {
                szCmd = NULL;
                dwOptions = CMDOPT_NOOP;
                pfnParse = ParseGetDevCapsPBIndexRangeNoop;
            }
            else {
                if (g_dwCurrentPhoneBook == RIL_PBLOC_SIMFIXDIALING && NO_READ_ON_FD_WO_PIN2 && !IsForbiddenSIMFile(SIM_EF_FDN)) {
                    char szPath[MAX_PATH];
                    BOOL fPath;

                    g_nSIMType = pHandle->GetDevice()->GetCurrentSIMType();
                    fPath = NeedSIMPath(g_nSIMType, SIM_EF_FDN, szPath, MAX_PATH);
                    if (fPath)
                        (void)_snprintfz(szCRSMCmd, MAX_ATCMD_LEN, "AT+CRSM=192,28475,0,0,%d,,\"%s\"\r", g_nSIMType == SIMTYPE_3G_SIM ? 63 : 15, szPath);
                    else
                        (void)_snprintfz(szCRSMCmd, MAX_ATCMD_LEN, "AT+CRSM=192,28475,0,0,%d\r", g_nSIMType == SIMTYPE_3G_SIM ? 63 : 15);
                    szCmd = szCRSMCmd;
                    pfnParse = ParseGetDevCapsPBIndexRangeCRSM;
                    dwOptions = CMDOPT_GENERATE_ERROR_CODE;
                    bDoRetry = TRUE;
                }
                else if (g_dwCurrentPhoneBook == RIL_PBLOC_SIMFIXDIALING && NO_READ_ON_FD_WO_PIN2 && IsForbiddenSIMFile(SIM_EF_FDN)) {
                    szCmd = NULL;
                    dwOptions = CMDOPT_NOOP;
                    pfnParse = ParseGetDevCapsPBIndexRangeNoop;
                }
                else {
                    szCmd = "AT+CPBR=?\r";
                    pfnParse = ParseGetDevCapsPBIndexRange;
                    dwOptions = CMDOPT_GENERATE_ERROR_CODE;
                    bDoRetry = TRUE;
                }
            }
            break;

        case RIL_CAPSTYPE_PBENTRYLENGTH:
            if (pHandle->GetDevice()->PhoneBookAccessDisabled()) {
                szCmd = NULL;
                dwOptions = CMDOPT_NOOP;
                pfnParse = ParseGetDevCapsPBEntryLengthNoop;
            }
            else {
                if (g_dwCurrentPhoneBook == RIL_PBLOC_SIMFIXDIALING && NO_READ_ON_FD_WO_PIN2 && !IsForbiddenSIMFile(SIM_EF_FDN)) {
                    char szPath[MAX_PATH];
                    BOOL fPath;

                    g_nSIMType = pHandle->GetDevice()->GetCurrentSIMType();
                    fPath = NeedSIMPath(g_nSIMType, SIM_EF_FDN, szPath, MAX_PATH);
                    if (fPath)
                        (void)_snprintfz(szCRSMCmd, MAX_ATCMD_LEN, "AT+CRSM=192,28475,0,0,%d,,\"%s\"\r", g_nSIMType == SIMTYPE_3G_SIM ? 63 : 15, szPath);
                    else
                        (void)_snprintfz(szCRSMCmd, MAX_ATCMD_LEN, "AT+CRSM=192,28475,0,0,%d\r", g_nSIMType == SIMTYPE_3G_SIM ? 63 : 15);
                    szCmd = szCRSMCmd;
                    pfnParse = ParseGetDevCapsPBEntryLengthCRSM;
                    dwOptions = CMDOPT_GENERATE_ERROR_CODE;
                    bDoRetry = TRUE;
                }
                else if (g_dwCurrentPhoneBook == RIL_PBLOC_SIMFIXDIALING && NO_READ_ON_FD_WO_PIN2 && IsForbiddenSIMFile(SIM_EF_FDN)) {
                    szCmd = NULL;
                    dwOptions = CMDOPT_NOOP;
                    pfnParse = ParseGetDevCapsPBEntryLengthNoop;
                }
                else {
                    szCmd = "AT+CPBR=?\r";
                    pfnParse = ParseGetDevCapsPBEntryLength;
                    dwOptions = CMDOPT_GENERATE_ERROR_CODE;
                    bDoRetry = TRUE;
                }
            }
            break;

        case RIL_CAPSTYPE_MSGSERVICETYPES:
            szCmd = "AT+CSMS=?\r";
            pfnParse = ParseGetDevCapsMsgSvcTypes;
            dwOptions = CMDOPT_GENERATE_ERROR_CODE;
            bDoRetry = TRUE;
            break;

        case RIL_CAPSTYPE_MSGMEMORYLOCATIONS:
            if (CANT_USE_CPMS) {
                szCmd = NULL;
                dwOptions = CMDOPT_NOOP;
                pfnParse = ParseGetDevCapsMsgMemoryLocsNoop;
            }
            else {
                szCmd = "AT+CPMS=?\r";
                pfnParse = ParseGetDevCapsMsgMemoryLocs;
                dwOptions = CMDOPT_GENERATE_ERROR_CODE;
                bDoRetry = TRUE;
            }
            break;

        case RIL_CAPSTYPE_BROADCASTMSGLANGS:
            szCmd = NULL;
            dwOptions = CMDOPT_NOOP;
            pfnParse = ParseGetDevCapsBroadcastMsgLangs;
            break;

        case RIL_CAPSTYPE_MSGCONFIGINDEXRANGE:
            szCmd = NULL;
            dwOptions = CMDOPT_NOOP;
            pfnParse = ParseGetDevCapsMsgConfigIndexRange;
            break;

        case RIL_CAPSTYPE_MSGSTATUSVALUES:
            szCmd = "AT+CMGL=?\r";
            pfnParse = ParseGetDevCapsMsgStatusValues;
            dwOptions = CMDOPT_GENERATE_ERROR_CODE;
            break;

        case RIL_CAPSTYPE_PREFOPINDEXRANGE:
            if ((g_dwReadyState & (RIL_READYSTATE_SIM|RIL_READYSTATE_UNLOCKED|RIL_READYSTATE_SMS)) != (RIL_READYSTATE_SIM|RIL_READYSTATE_UNLOCKED|RIL_READYSTATE_SMS)) {
                // If the SIM is busy we must not send AT+CPOL=?, otherwise it would never work again.
                szCmd = NULL;
                dwOptions = CMDOPT_NOOP;
                pfnParse = ParseForceFailureResponse;
                pParam = (LPVOID)RIL_E_SIMBUSY;
            }
            else {
                szCmd = "AT+CPOL=?\r";
                nCmdMode = HCCMD_URCPORT_ONLY;
                dwOptions = CMDOPT_GENERATE_ERROR_CODE;
                pfnParse = ParseGetDevCapsPrefOpIndexRange;
                if (WRONG_SIM_FAILURE_AT_CPOL)   // The PH8 might give a "SIM failure" response, which the Microsoft MMI will misinterprete.
                    pfnParseError = ParseGetDevCapsPrefOpIndexRangeError;
                bSendFollowingAT = CPOL_RESPONSE_ON_WRONG_PORT;
            }
            break;

        case RIL_CAPSTYPE_LOCKFACILITIES:
            szCmd = NULL;
            dwOptions = CMDOPT_NOOP;
            pfnParse = ParseGetDevCapsLockFacilities;
            break;

        case RIL_CAPSTYPE_LOCKINGPWDLENGTHS:
            szCmd = "AT+CPWD=?\r";
            pfnParse = ParseGetDevCapsLockPwdLengths;
            dwOptions = CMDOPT_GENERATE_ERROR_CODE;
            break;

        case RIL_CAPSTYPE_BARRTYPES:
            szCmd = "AT+CLCK=?\r";
            pfnParse = ParseGetDevCapsBarrTypes;
            dwOptions = CMDOPT_GENERATE_ERROR_CODE;
            break;

        case RIL_CAPSTYPE_BARRINGPWDLENGTHS:
            szCmd = "AT+CPWD=?\r";
            pfnParse = ParseGetDevCapsBarrPwdLengths;
            dwOptions = CMDOPT_GENERATE_ERROR_CODE;
            break;

        case RIL_CAPSTYPE_FORWARDINGREASONS:
			if(SUPPORTS_ONLY_DATAMODE){
				hr = E_NOTIMPL;
			}else{
	            szCmd = NULL;
	            dwOptions = CMDOPT_NOOP;
	            pfnParse = ParseGetDevCapsFwdReasons;
			}
            break;

        case RIL_CAPSTYPE_INFOCLASSES:
            szCmd = NULL;
            dwOptions = CMDOPT_NOOP;
            pfnParse = ParseGetDevCapsInfoClasses;
            break;

        case RIL_CAPSTYPE_HSCSD:
            hr = E_NOTIMPL;
            goto Error;

        case RIL_CAPSTYPE_SIMTOOLKITNOTIFICATIONS:
            szCmd = NULL;
            dwOptions = CMDOPT_NOOP;
            pfnParse = (PFN_CMD_PARSE)pHandle->GetDevice()->GetSimToolkit()->ParseGetDevCapsNotifications;
            break;

        case RIL_CAPSTYPE_GPRSCLASS:
            szCmd = NULL;
            dwOptions = CMDOPT_NOOP;
            pfnParse = ParseGetGprsCapsClass;
            break;
        case RIL_CAPSTYPE_GPRSCONTEXT:
            szCmd = NULL;
            dwOptions = CMDOPT_NOOP;
            pfnParse = ParseGetGprsCapsContext;
            fDataPort = FALSE;
            break;
        case RIL_CAPSTYPE_GPRSQOS:
            szCmd = "AT+CGQREQ=?\r";
            pfnParse = ParseGetGprsCapsQOS;
            dwOptions = CMDOPT_GENERATE_ERROR_CODE;
            fDataPort = TRUE;
            break;
        case RIL_CAPSTYPE_GPRSQOSMIN:
            szCmd = "AT+CGQMIN=?\r";
            pfnParse = ParseGetGprsCapsQOSMin;
            dwOptions = CMDOPT_GENERATE_ERROR_CODE;
            fDataPort = TRUE;
            break;
        case RIL_CAPSTYPE_GPRSMOSMS:
            szCmd = "AT+CGSMS=?\r";
            pfnParse = ParseGetGprsCapsSMOSMS;
            dwOptions = CMDOPT_GENERATE_ERROR_CODE;
            break;

        case RIL_CAPSTYPE_DATACOMPRESSION:
            hr = E_NOTIMPL;
            goto Error;

        case RIL_CAPSTYPE_ERRORCORRECTION:
            hr = E_NOTIMPL;
            goto Error;

        case RIL_CAPSTYPE_SIGNALQUALITYIMPLEMENTATION:
            szCmd = NULL;
            dwOptions = CMDOPT_NOOP;
            pfnParse = ParseGetSignalQualityImplemenation;
            break;

        case RIL_CAPSTYPE_SIMSUPPORT:
            szCmd = NULL;
            dwOptions = CMDOPT_NOOP;
            pfnParse = ParseGetDevCapsSIMSupport;
            break;

        case RIL_CAPSTYPE_CALLPROGRESSNOTIFICATION:
            szCmd = NULL;
            dwOptions = CMDOPT_NOOP;
            pfnParse = ParseGetCallProgressNotification;
            break;

#ifdef RIL_NOTIFY_NITZ
        case RIL_CAPSTYPE_NITZNOTIFICATION:
            szCmd = NULL;
            dwOptions = CMDOPT_NOOP;
            pfnParse = ParseGetNITZSupport;
            break;
#endif

#ifdef RIL_CAPSTYPE_PHONECAPABILITY
        case RIL_CAPSTYPE_PHONECAPABILITY:
            szCmd = NULL;
            dwOptions = CMDOPT_NOOP;
            pfnParse = ParseGetPhoneCapability;
            break;
#endif

#ifdef RIL_CAPSTYPE_NOTIFICATIONIMPLEMENTATION
        case RIL_CAPSTYPE_NOTIFICATIONIMPLEMENTATION:
            szCmd = NULL;
            dwOptions = CMDOPT_NOOP;
            pfnParse = ParseGetNotificationImplemenation;
            break;
#endif

#ifdef RIL_CAPSTYPE_CALLSUPPORT
        case RIL_CAPSTYPE_CALLSUPPORT:
            szCmd = NULL;
            dwOptions = CMDOPT_NOOP;
            pfnParse = ParseGetCallSupportImplemenation;
            break;
#endif

#ifdef RIL_CAPSTYPE_SMSSUPPORT
        case RIL_CAPSTYPE_SMSSUPPORT:
            szCmd = NULL;
            dwOptions = CMDOPT_NOOP;
            pfnParse = ParseGetSMSSupportImplemenation;
            break;
#endif

#ifdef RIL_CAPSTYPE_MSGINDEXRANGE
        case RIL_CAPSTYPE_MSGINDEXRANGE:
            szCmd = "AT+CPMS?\r";
            pfnParse = ParseMsgIndexRange;
            dwOptions = CMDOPT_GENERATE_ERROR_CODE;
            fDataPort = TRUE;
            break;
#endif

        default:
            hr = E_INVALIDARG;
            goto Error;
    }

    if (fDataPort == TRUE) {
        if (!QueueCmd(HCCMD_STANDARD, pHandle, szCmd, dwOptions, APIID_GETDEVCAPS, pfnParse, NULL, hr, pfnParseError, pParam)) {
            hr = E_FAIL;
            goto Error;
        }
    }
    else {
        if (bDoRetry == TRUE) {
            if (g_PowerManager.IsRadioOff() || (g_dwReadyState & (RIL_READYSTATE_UNLOCKED))  != (RIL_READYSTATE_UNLOCKED))
            {
                // If the radio is off or the radio is at least unlocked, it doesn't make sense to execute this command.
                if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_GETDEVCAPS, ParseForceFailureResponse, NULL, hr, NULL, (LPVOID)(g_PowerManager.IsRadioOff() ? RIL_E_RADIOOFF : RIL_E_SIMBUSY))) {
                    hr = E_FAIL;
                    goto Error;
                }
                bSendFollowingAT = FALSE;
            }
            else {
                DWORD dwRetries = 8;
                DWORD dwDelay = 5000;

                (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("CapsCommandRetries"), &dwRetries);
                (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("CapsCommandRetryDelay"), &dwDelay);
                // This command may be called, when the SIM is not ready yet, so we may get a
                // 'SIM busy' error. In this case, we just try it again.
                if (!QueueCmdWithRetry(nCmdMode, pHandle, szCmd, dwOptions, APIID_GETDEVCAPS, pfnParse, NULL, hr, dwRetries, dwDelay, pfnParseError, pParam)) {
                    hr = E_FAIL;
                    goto Error;
                }
            }
        }
        else {
            if (!QueueCmd(nCmdMode, pHandle, szCmd, dwOptions, APIID_GETDEVCAPS, pfnParse, NULL, hr, pfnParseError, pParam)) {
                hr = E_FAIL;
                goto Error;
            }
        }
    }

    if (bSendFollowingAT) {
        // There might the problem, that there some unwonted response on the URC port. In order
        // to not mess up the response of the next command, the RIL sends this dummy command.
        (void)QueueInternalCmd(HCCMD_STANDARD, pHandle->GetDevice(), "AT\r", CMDOPT_NONE, APIID_GETDEVCAPS, 0, 0, 0, ParseGetDevCapsPrefOpIndexRange, NULL, NULL);
    }

Error:
    return hr;
}

