/*****************************************************************************
 * Copyright (C) Gemalto M2M GmbH
 *****************************************************************************
 * Project: IMC-RIL-Driver
 *****************************************************************************
 * Author : Wireless Modules RD SW A2
 *****************************************************************************
 * $Workfile: Y:/z_ms-d/src/working/Quinn/RilDrv/ConfigRadio.cpp $
 * $Revision: /main/ms-d/89 $
 *     $Date: 2013-10-24 13:50:29 $
 *   $Author: pmackent $
 *****************************************************************************
 * Target:
 *  Microsoft Windows Mobile 5.0
 *****************************************************************************
 * Description:
 *  Gemalto IMC RIL
 *****************************************************************************
 *
 * $Log: Y:/z_ms-d/src/working/Quinn/RilDrv/ConfigRadio.cpp$ 
 * 
 * /main/ms-d/89   2013-10-24 13:50:29   pmackent 
 * Added missing entries for BGS5
 *
 * /main/ms-d/88   2013-10-24 13:41:21   pmackent
 * Preparation to support Windows Embedded Compact 2013
 *
 * /main/ms-d/87   2013-09-13 09:26:57   pmackent
 * Cell Broadcast for CDMA (IPIS100091379)
 *
 * /main/ms-d/86   2013-07-23 13:20:24   pmackent
 * Support EHS5 Rel2
 *
 * /main/ms-d/85   2013-06-27 16:00:07   pmackent
 * Adaptions for EHS5 Rel1->Rel2 update
 *
 * /main/ms-d/84   2013-06-10 09:21:30   pmackent
 * Use AT^SCFG instad of AT^SGPSC when setting the GPRS autoattach setting (IPIS100088749)
 *
 * /main/ms-d/83   2013-05-23 11:56:02   pmackent
 * Handle AT^SCFG and AT^SGPSC in one function
 *
 * /main/ms-d/82   2013-05-21 15:51:39   pmackent
 * Add BGS5 as known module for testing purposes
 *
 * /main/ms-d/81   2013-05-17 14:59:13   pmackent
 * Make technology switch thread-safe
 *
 * /main/ms-d/80   2013-05-14 16:26:58   pmackent
 * GPS/GLONASS setting need reboot; ensure GPS antenna is on auto-mode
 *
 * /main/ms-d/79   2013-05-14 14:35:03   pmackent
 * Option to activate OMALOG in Sprint (IPIS100087489)
 *
 * /main/ms-d/78   2013-05-07 14:16:54   pmackent
 * New GPIO setting in EHS5
 *
 * /main/ms-d/77   2013-04-24 11:44:36   pmackent
 * New CDMA/CNMI setting and CDMA/CLCC workaround
 *
 * /main/ms-d/76   2013-04-22 16:12:11   pmackent
 * Work on workarounds for AT+COPS/<AcT> and AT^SJDL/ATE
 *
 * /main/ms-d/75   2013-04-17 13:36:57   pmackent
 * New reigistry key "EarlyPreInitCommandOnce" (IPIS100086851)
 *
 * /main/ms-d/74   2013-04-05 15:05:10   pmackent
 * Missing call waiting in CDMA (IPIS100086535)
 *
 * /main/ms-d/73   2013-04-05 09:53:31   pmackent
 * Finish EHS5 Java midlet update
 * Add RHA-API for bootloader pin on EHS5
 *
 * /main/ms-d/72   2013-03-25 14:43:06   pmackent
 * Small adaptions for PXS8 multimode
 *
 * /main/ms-d/71   2013-03-14 09:55:14   pmackent
 * Add configuration for tethered NAI
 *
 * /main/ms-d/70   2013-03-13 15:40:06   pmackent
 * Allow internal Sprint PRL file
 *
 * /main/ms-d/69   2013-03-05 11:33:32   pmackent
 * Handle ^SYSSTART URC for EHS5 Rel2
 *
 * /main/ms-d/68   2013-02-28 16:11:29   pmackent
 * Add debug output for EHS5 Rel1 support
 *
 * /main/ms-d/67   2013-02-27 15:18:12   pmackent
 * Introduce provider spezific PRL update
 *
 * /main/ms-d/66   2013-01-23 09:37:54   pmackent
 * Corrected string length calculation
 *
 * /main/ms-d/64   2013-01-21 10:55:40   pmackent
 * FS update got renamed to EFS
 *
 * /main/ms-d/63   2013-01-16 11:28:52   pmackent
 * Introduce update of module file system
 *
 * /main/ms-d/62   2013-01-14 11:28:43   pmackent
 * Avoid overwriting the PRL with factiry default (IPIS100084373)
 *
 * /main/ms-d/61   2013-01-14 10:33:40   pmackent
 * Allow update of multiple NV items
 *
 * /main/ms-d/60   2012-12-21 13:08:59   pmackent
 * Introduce NV item update
 *
 * /main/ms-d/59   2012-12-12 15:23:57   pmackent
 * Workarounds for bugs in PXS8 2.821
 *
 * /main/ms-d/58   2012-12-07 11:35:57   pmackent
 * Check module type of firmware file (IPIS100083958)
 *
 * /main/ms-d/57   2012-11-23 16:54:49   pmackent
 * Additional adaptions for EHS5
 *
 * /main/ms-d/56   2012-11-22 10:34:32   pmackent
 * Workaround: no AT+GMM in EHS5, no AT+CGMM in PXS8 (CDMA)
 *
 * /main/ms-d/55   2012-11-21 14:25:21   pmackent
 * Use +GMM/+GMR instead of +CGMM/+CGMR because they won't work in PXS8 airplane mode
 *
 * /main/ms-d/54   2012-11-21 13:30:10   pmackent
 * Basic support of EHS5 Rel2
 *
 * /main/ms-d/53   2012-11-19 15:37:39   pmackent
 * Add EHS5 variants
 *
 * /main/ms-d/52   2012-11-16 12:48:39   pmackent
 * Documentation for ResetACQDB
 *
 * /main/ms-d/51   2012-11-15 11:45:05   pmackent
 * Avoid sending AT+CSQ in D3 power state (IPIS100083487)
 *
 * /main/ms-d/50   2012-11-13 13:52:56   pmackent
 * New option: reser of acquisition database (IPIS100083325)
 *
 * /main/ms-d/49   2012-11-01 17:12:43   pmackent
 * Retries for SCFG configuration
 *
 * /main/ms-d/48   2012-10-30 14:39:26   pmackent
 * Reg-Key for PRI file format
 *
 * /main/ms-d/47   2012-10-17 15:22:56   pmackent
 * Fix for missing NITZ/CCLK in CDMA mode (IPIS100082923)
 *
 * /main/ms-d/46   2012-10-12 13:14:57   pmackent
 * New syntax for AT^SBNW=PRI and PRL
 *
 * /main/ms-d/45   2012-10-08 16:18:18   pmackent
 * Comply to Klocwork checks.
 *
 * /main/ms-d/44   2012-09-05 10:48:15   pmackent
 * PRL Update only if file is newer than module list
 *
 * /main/ms-d/43   2012-09-04 11:15:37   pmackent
 * Set default PXS8 mode to multimode
 *
 * /main/ms-d/42   2012-08-28 09:44:32   pmackent
 * Allow Rel3 of Pxx8-family modules
 *
 * /main/ms-d/41   2012-08-27 16:18:01   pmackent
 * Write SPC before PRI update (optional)
 *
 * /main/ms-d/40   2012-08-24 10:29:45   pmackent
 * Use CTZU for CDMA
 *
 * /main/ms-d/39   2012-08-13 11:03:48   pmackent
 * Comply to CE6 code analyzer requirements
 *
 * /main/ms-d/38   2012-07-13 13:04:23   pmackent
 * Changes for compatibility with Windows CE6/6
 *
 * /main/ms-d/37   2012-07-11 15:28:31   pmackent
 * Introduce PRI update
 *
 * /main/ms-d/36   2012-07-10 11:53:04   pmackent
 * Introduce AHS3
 *
 * /main/ms-d/35   2012-07-03 11:51:57   pmackent
 * Introduce infor about provider list date
 *
 * /main/ms-d/34   2012-06-26 10:15:42   pmackent
 * Change default for CDMA update delay
 *
 * /main/ms-d/33   2012-06-21 13:16:20   pmackent
 * Introduce CDMA PRL list update
 *
 * /main/ms-d/32   2012-05-16 10:40:27   pmackent
 * New names of the options of for AT^SCFG=MeOpMode/AccT
 *
 * /main/ms-d/31   2012-04-18 13:46:48   ctna0256
 * Introducing PVS8
 * Implementing CDMA-SMS
 * Various CDMA enhancements
 *
 * /main/ms-d/30   2012-02-22 13:26:32   ctna0256
 * Encapsulate use of Intel AT command set
 *
 * /main/ms-d/29   2012-02-17 11:47:08   ctna0256
 * Increase string length for SCFG settings
 *
 * /main/ms-d/28   2012-02-16 13:44:54   ctna0256
 * Comply to CE6 and CE7 compiler warnings
 *
 * /main/ms-d/27   2012-02-14 14:56:40   ctna0256
 * Initialize module version with defauts.
 *
 * /main/ms-d/26   2012-02-02 17:04:08   ctna0256
 * FD phonebook workaround and NITZ URCs for Intel platform
 *
 * /main/ms-d/25   2012-01-31 16:38:19   ctna0256
 * Added support for EHS5
 *
 * /main/ms-d/24   2012-01-12 15:22:59   ctna0256
 * Some basic CDMA adaptions
 *
 * /main/ms-d/23   2012-01-05 14:56:37   ctna0256
 * New RHA-API for CDMA/UMTS technology switch
 *
 * /main/ms-d/22   2012-01-04 15:36:46   ctna0256
 * Outsource XML configuration to external application.
 * Improve CDMA configuration mode.
 *
 * /main/ms-d/21   2012-01-03 16:37:43   ctna0256
 * Optional processing of config XML on technology change
 *
 * /main/ms-d/20   2012-01-03 15:46:34   ctna0256
 * Introduce PXS8
 *
 * /main/ms-d/19   2011-11-14 10:46:13   ctna0256
 * Additional configurations
 *
 * /main/ms-d/18   2011-10-11 14:19:53   ctna0256
 * Add PHS8
 *
 * /main/ms-d/17   2011-09-09 14:11:36   ctna0256
 * Support for PHS8 (IPIS100074765)
 *
 * /main/ms-d/16   2011-08-29 15:23:26   ctna0256
 * Change output of version numbers
 *
 * /main/ms-d/14   2011-07-22 09:30:19   ctna0256
 * Add support of PH8-P (IPIS100073860)
 *
 * /main/ms-d/13   2011-07-14 13:52:30   ctna0256
 * Changed comments and max version of PH8
 *
 * /main/ms-d/12   2011-05-30 10:11:38   ctna0256
 * Avoid SLCC on PH8 Rel1
 *
 * /main/ms-d/11   2011-03-03 14:14:43   ctna0256
 * Allow development versions of PH8 Rel2
 *
 * /main/ms-d/10   2011-03-01 11:15:05   ctna0256
 * Introduce define for EF_CSP to improve source code readability
 *
 * /main/ms-d/9   2010-12-20 10:03:22   ctna0256
 * Allow versions PH8 > 1.000
 *
 * /main/ms-d/8   2010-12-15 17:08:28   ctna0256
 * Restart on GPRS-Attach config change
 *
 * /main/ms-d/7   2010-12-15 14:58:02   ctna0256
 * Allow overwrite of radio version and type
 *
 * /main/ms-d/6   2010-12-14 10:15:13   ctna0256
 * Fix problem with restart on changed settings and resets on SAD
 *
 * /main/ms-d/5   2010-12-13 16:06:58   ctna0256
 * Allow automatic restart on configuration changes
 *
 * /main/ms-d/4   2010-12-10 14:40:27   ctna0256
 * Remove configuration of USB voltage
 *
 * /main/ms-d/3   2010-12-02 14:52:23   ctna0256
 * Key for early pre-init
 *
 * /main/ms-d/2   2010-11-29 09:26:51   ctna0256
 * Configuration of USB voltage
 *
 * /main/ms-d/1   2010-11-26 10:13:12   ctna0256
 * Moved radio configuration from misc.cpp to new file
 *
 *
*/

//////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2010 Gemalto M2M GmbH
//
// Version: 1.0
// Date:    dummy
//
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//
// Additional includes
//
//////////////////////////////////////////////////////////////////////////////

#include "precomp.h"
#include "..\..\inc\rilversion.h"

//////////////////////////////////////////////////////////////////////////////
//
// Global variables, external declarations and constants
//
//////////////////////////////////////////////////////////////////////////////

// List of suported module types.
static const SUPPORTED_MODEM g_SupportedModems[] = {
    { "HC25A",   MODEMTYPE_HC28},           // The HC25A can be treated exactly like the HC28
    { "HC25",    MODEMTYPE_HC25},
    { "HC28-J",  MODEMTYPE_HC28},           // The HC28-J can be treated exactly like the HC28
    { "HC28",    MODEMTYPE_HC28},
    { "EU3-E",   MODEMTYPE_EU3},            // Unsupported module, entry used for testing purposes only.
    { "EU3-P",   MODEMTYPE_EU3},            // Unsupported module, entry used for testing purposes only.
    { "PH8-P",   MODEMTYPE_PH8},            // PH8-P can be treated exactly like the PH8
    { "PH8",     MODEMTYPE_PH8},
    { "PHS8-P",  MODEMTYPE_PH8},            // The PHS8 and all its variations can be handled like the PH8
    { "PHS8-E",  MODEMTYPE_PH8},
    { "PHS8-US", MODEMTYPE_PH8},
    { "AHS3-W",  MODEMTYPE_AHS3},           // The AHS3 and all its variations can be handled the same way
    { "AHS3-US", MODEMTYPE_AHS3},
    { "PHS8",    MODEMTYPE_PH8},
    { "PXS8",    MODEMTYPE_PXS8},
    { "PVS8",    MODEMTYPE_PVS8},
    { "EHS5-E",  MODEMTYPE_EHS5},           // The two EHS5 variants can be handled the same way
    { "EHS5-US", MODEMTYPE_EHS5},
    { "EHS6",    MODEMTYPE_EHS5},           // The EHS6 can be handled like an EHS5
    { "BGS5",    MODEMTYPE_BGS5},           // For testing purposes only: BGS5 is the GPRS-only version of the EHS5
    { "EHS8",	 MODEMTYPE_EHS8},			// Add for EHS8 which integrated GPS on it, Jarkata
    { "PDS5-E",	 MODEMTYPE_PDS5},
    { "PDS5-US", MODEMTYPE_PDS5},
    { "PDS6", 	 MODEMTYPE_PDS5},
    { "PDS8",	 MODEMTYPE_PDS8},
	{ "ELS61-E", MODEMTYPE_ELS61_E},       //Boxwood Rel1,3,4
	{ "ELS61-USA", MODEMTYPE_ELS61_USA},   //Boxwood Rel3
    { "ELS61-US",MODEMTYPE_ELS61_US},      //Boxwood Rel1,3,4
	{ "ELS61-AUS", MODEMTYPE_ELS61_AUS},   //KIWI
	{ "PLS62-W", MODEMTYPE_PLS62},         //Ginger
	{ "ELS81-E", MODEMTYPE_ELS81_E},       //Boxwood5
    { "ELS81-US",MODEMTYPE_ELS81_US},      //Boxwood5
#ifdef SUPPORTS_EHSX_R1
    { " XMM6260",MODEMTYPE_EHS5},           // Prototype for the future EHS5 family
#endif
    { NULL,      MODEMTYPE_UNKNOWN}
};
// Specifies supported module firmware versions.
static const DWORD g_dwMinModemVersion[NUMBER_OF_SUPPORTED_MODEMTYPES] = {
    0,        // MODEMTYPE_UNKNOWN
    0,        // MODEMTYPE_HC25
    0,        // MODEMTYPE_HC28
    0,        // MODEMTYPE_EU3
    100,      // MODEMTYPE_PH8 (earlier customer samples need special RIL)
    100,      // MODEMTYPE_PXS8
    100,      // MODEMTYPE_PVS8
    0,        // MODEMTYPE_EHS5
    0,        // MODEMTYPE_BGS5
    0,        // MODEMTYPE_AHS3
    0,		  // MODEMTYPE_EHS8
    0,		  // MODEMTYPE_PDS5
    0,		  // MODEMTYPE_PDS8
	0,		  // MODEMTYPE_ELS61_E,
	0,		  // MODEMTYPE_ELS61_USA,
	0,		  // MODEMTYPE_ELS61_US,
	0,		  // MODEMTYPE_ELS61_AUS,
    0 ,        // MODEMTYPE_PLS62
	0,		  // MODEMTYPE_ELS81_E,
	0 		  // MODEMTYPE_ELS81_US,
};
static const DWORD g_dwMaxModemVersion[NUMBER_OF_SUPPORTED_MODEMTYPES] = {
    0,        // MODEMTYPE_UNKNOWN
    2099,     // MODEMTYPE_HC25
    2299,     // MODEMTYPE_HC28
    1999,     // MODEMTYPE_EU3
    3099,     // MODEMTYPE_PH8
    3099,     // MODEMTYPE_PXS8
    3099,     // MODEMTYPE_PVS8
    4999,     // MODEMTYPE_EHS5
    2099,     // MODEMTYPE_BGS5
    3099,     // MODEMTYPE_AHS3
    4999,	  // MODEMTYPE_EHS8
    4999,	  // MODEMTYPE_PDS5
    4999,	  // MODEMTYPE_PDS8
	1999,	  // MODEMTYPE_ELS61_E,
	1999,	  // MODEMTYPE_ELS61_US,
	1999,	  // MODEMTYPE_ELS61_USA,
	1999,	  // MODEMTYPE_ELS61_AUS,
    1999,	  // MODEMTYPE_PLS62
	1999,	  // MODEMTYPE_ELS81_E,
	1999	  // MODEMTYPE_ELS81_US,
};

// Optional: Use built-in PRL list for Sprint. Use the tool "WriteRILPRL" to fill
// in a Sprint PRL list.
static unsigned int s_uiSizeSprintPRL = 0;
static unsigned int s_uiVersionSprintPRL = 0;
static const unsigned char s_bSprintPRL[] = {
    0x00 };
#define INTERNAL_PRL_SPRINT L"INTERNAL:Sprint"

// Same as above, but for Verizon
static unsigned int s_uiSizeVerizonPRL = 0;
static unsigned int s_uiVersionVerizonPRL = 0;
static const unsigned char s_bVerizonPRL[] = {
    0x00 };
#define INTERNAL_PRL_VERIZON L"INTERNAL:Verizon"

// Maximum string length for an SCFG setting.
#define SCFG_MAX_SETTING_LEN  32

UINT g_uiECCModule = 39;
DWORD g_dwEnableGPRSAttach = 0;
DWORD g_dwEnableGPRSReAttach = 0;

// Short time (in ms) to make sure, a configuration change is actually stored by the radio.
#define WAIT_FOR_CONFIG_WRITE     1000

// Default PRL list update timings:
// CDMA_PRL_UPDATE_TIMEOUT: Timeout for AT commands used for PRL list update
// CDMA_PRL_UPDATE_DELAY: Delay for each byte of the PRL list (needed for USB or other fast systems)
// CDMA_PRL_UPDATE_INIT_DELAY: Delay before download of first byte starts (needed, because the module is not immediately ready)
#define CDMA_PRL_UPDATE_TIMEOUT    20000
#define CDMA_PRL_UPDATE_DELAY      0
#define CDMA_PRL_UPDATE_INIT_DELAY   2000
// Default PRI configuration update timings:
// Same as above, but for PRI update
#define CDMA_PRI_UPDATE_TIMEOUT    20000
#define CDMA_PRI_UPDATE_DELAY      0
#define CDMA_PRI_UPDATE_INIT_DELAY   2000
// Same as above, but for NV item update
#define NV_UPDATE_TIMEOUT    20000
#define NV_UPDATE_DELAY      0
#define NV_UPDATE_INIT_DELAY   2000
// Same as above, but for FS item update
#define FS_UPDATE_TIMEOUT    20000
#define FS_UPDATE_DELAY      0
#define FS_UPDATE_INIT_DELAY   2000

//////////////////////////////////////////////////////////////////////////////
//
// Functions
//
//////////////////////////////////////////////////////////////////////////////

//
// Extracts the version info from the resources of a DLL.
//
// Parameters:
//     szDllName [in]: filename of the DLL in question
//     szPrivateBuild [out]: string with the private build info of the DLL (optional)
//     dwPBLen [in]: in optional szPrivateBuild is used, size of buffer.
//
// Return value: version in DWORD format (eg. 1.2.3.4 will be returned as 0x01020304)
//
DWORD GetDllVersionInfo(LPTSTR szDllName, LPTSTR szPrivateBuild, DWORD dwPBLen) {
DWORD              dwVersion = (DWORD)-1;
LPBYTE             VerInfo;
VS_FIXEDFILEINFO*  pFixedInfo;
UINT               uiLen;
DWORD              dwSize, dwHandle;

  if (szPrivateBuild && dwPBLen) {
    szPrivateBuild[dwPBLen - 1] = TEXT('\0');
    _tcsncpy(szPrivateBuild, TEXT("OS Build unknown"), dwPBLen - 1);
  }

  dwSize = GetFileVersionInfoSize(szDllName, &dwHandle);
  if (dwSize) {
    VerInfo = new BYTE[dwSize];
    if (VerInfo) {
      if (GetFileVersionInfo(szDllName, 0, dwSize, VerInfo)) {
        if (VerQueryValue(VerInfo, TEXT("\\"), (LPVOID*)(&pFixedInfo), &uiLen)) {
          dwVersion = 0;
          dwVersion |= ((BYTE)pFixedInfo->dwProductVersionLS);
          dwVersion |= ((BYTE)(pFixedInfo->dwProductVersionLS >> 16) << 8);
          dwVersion |= ((BYTE)pFixedInfo->dwProductVersionMS) << 16;
          dwVersion |= ((BYTE)(pFixedInfo->dwProductVersionMS >> 16)) << 24;
        }
        if (szPrivateBuild && dwPBLen) {
          TCHAR   szVerStrName[128];
          LPVOID  lpPB;
          UINT    uiTransLen;
          DWORD   i;
          struct LANGANDCODEPAGE {
            WORD wLanguage;
            WORD wCodePage;
          } *pTrans;
          if(VerQueryValue(VerInfo, _T("\\VarFileInfo\\Translation"), (LPVOID*)&pTrans, &uiTransLen)) {
            for (i = 0; i < (uiTransLen / sizeof(struct LANGANDCODEPAGE)); i++) {
              wsprintf(szVerStrName, _T("\\StringFileInfo\\%04x%04x\\PrivateBuild"), pTrans[i].wLanguage, pTrans[i].wCodePage);
              if (VerQueryValue(VerInfo, szVerStrName, &lpPB, &uiLen)) {
                _tcsncpy(szPrivateBuild, (LPTSTR)lpPB, dwPBLen - 1);
                break;
              }
            }
          }
        }
      }
      delete [] VerInfo;
    }
  }

  return dwVersion;
}

//
// Reads version of HcUSBDrv USB driver.
//
// Return value: version number
//
DWORD GetUsbVersion(void) {
  return GetDllVersionInfo(TEXT("HcUsbDrv.dll"), 0, 0);
}

//
// Reads version of CEMux multiplexer driver.
//
// Return value: version number
//
DWORD GetMuxVersion(void) {
  return GetDllVersionInfo(TEXT("multiplx.dll"), 0, 0);
}

//
// Prints out the version numbers of the installed drivers and the currently active debug zones.
//
void TraceDriverInfo(void) {
TCHAR  szPrivateBuild[80];
DWORD  dwDrvVer;
char szProviderInfo[PROVIDERLIST_DATE_STRING_LEN];

  dwDrvVer = RIL_VERSION_NUM;
#ifdef RILDRV_CE8
  RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: Current CE8-RIL Version: %d.%d.%d.%d (%a)\r\n"), (dwDrvVer >> 24) & 255, (dwDrvVer >> 16) & 255, (dwDrvVer >> 8) & 255, dwDrvVer & 255, RIL_BUILD_STRING));
#else
#ifdef RILDRV_CE7
  RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: Current CE7-RIL Version: %d.%d.%d.%d (%a)\r\n"), (dwDrvVer >> 24) & 255, (dwDrvVer >> 16) & 255, (dwDrvVer >> 8) & 255, dwDrvVer & 255, RIL_BUILD_STRING));
#else
#ifdef RILDRV_CE6
  RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: Current CE6-RIL Version: %d.%d.%d.%d (%a)\r\n"), (dwDrvVer >> 24) & 255, (dwDrvVer >> 16) & 255, (dwDrvVer >> 8) & 255, dwDrvVer & 255, RIL_BUILD_STRING));
#else
  RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: Current RIL Version: %d.%d.%d.%d (%a)\r\n"), (dwDrvVer >> 24) & 255, (dwDrvVer >> 16) & 255, (dwDrvVer >> 8) & 255, dwDrvVer & 255, RIL_BUILD_STRING));
#endif
#endif
#endif
#ifdef SUPPORTS_EHSX_R1
  RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: Support of EHS5 Release 1 is enabled.\r\n")));
#endif
  dwDrvVer = GetDllVersionInfo(TEXT("HcUsbDrv.dll"), szPrivateBuild, sizeof(szPrivateBuild) / sizeof(TCHAR));
  RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: Current Usb-Drv Version: %d.%d.%d.%d (%s)\r\n"), (dwDrvVer >> 24) & 255, (dwDrvVer >> 16) & 255, (dwDrvVer >> 8) & 255, dwDrvVer & 255, szPrivateBuild));
  dwDrvVer = GetDllVersionInfo(TEXT("HcCmdApi.dll"), szPrivateBuild, sizeof(szPrivateBuild) / sizeof(TCHAR));
  if (dwDrvVer != (DWORD)-1)
    RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: Current CmdApi Version: %d.%d.%d.%d (%s)\r\n"), (dwDrvVer >> 24) & 255, (dwDrvVer >> 16) & 255, (dwDrvVer >> 8) & 255, dwDrvVer & 255, szPrivateBuild));
  else
    RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: Current CmdApi Version: not installed\r\n")));
  dwDrvVer = GetDllVersionInfo(TEXT("multiplx.dll"), szPrivateBuild, sizeof(szPrivateBuild) / sizeof(TCHAR));
  if (dwDrvVer != (DWORD)-1) {
    if (_tcslen(szPrivateBuild) == 0)
      RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: Current Multiplexer Version: %d.%d.%d.%d\r\n"), (dwDrvVer >> 24) & 255, (dwDrvVer >> 16) & 255, (dwDrvVer >> 8) & 255, dwDrvVer & 255));
    else
      RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: Current Multiplexer Version: %d.%d.%d.%d (%s)\r\n"), (dwDrvVer >> 24) & 255, (dwDrvVer >> 16) & 255, (dwDrvVer >> 8) & 255, dwDrvVer & 255, szPrivateBuild));
  }
  else {
    RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: Current Multiplexer Version: not installed\r\n")));
  }
  if (GetProviderListInfo(szProviderInfo)) {
     RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: Current Provider List Version: %a\r\n"), szProviderInfo));
  }
  if (g_dwDebugZones != 0) {
    char szZones[MAX_ATCMD_LEN];

    memset(szZones, 0, sizeof(szZones));
    if (g_dwDebugZones&ZONE_ATCOMMAND)
        (void)strncatz(szZones, "ATCMD ", MAX_ATCMD_LEN);
    if (g_dwDebugZones&ZONE_ERROR)
        (void)strncatz(szZones, "ERROR ", MAX_ATCMD_LEN);
    if (g_dwDebugZones&ZONE_INFO)
        (void)strncatz(szZones, "INFO ", MAX_ATCMD_LEN);
    if (g_dwDebugZones&ZONE_IOCTL)
        (void)strncatz(szZones, "IOCTL ", MAX_ATCMD_LEN);
    if (g_dwDebugZones&ZONE_NMEA)
        (void)strncatz(szZones, "NMEA ", MAX_ATCMD_LEN);
    if (g_dwDebugZones&ZONE_NOTIFICATION)
        (void)strncatz(szZones, "NOTIFY ", MAX_ATCMD_LEN);
    if (g_dwDebugZones&ZONE_RHA)
        (void)strncatz(szZones, "RHA ", MAX_ATCMD_LEN);
    if (g_dwDebugZones&ZONE_RHA_POWER)
        (void)strncatz(szZones, "RHA_POWER ", MAX_ATCMD_LEN);
    if (g_dwDebugZones&ZONE_PORT)
        (void)strncatz(szZones, "PORT ", MAX_ATCMD_LEN);
#ifdef RIL_ALLOW_TRACE_PPP_DATA
    if (g_dwDebugZones&ZONE_DATA)
        (void)strncatz(szZones, "DATA ", MAX_ATCMD_LEN);
#endif
    RILRetailTrace((TEXT("RilDrv: Used trace options: %a\r\n"), szZones));
  }
}

//
// Checks, ich the current module response contains the name of a known module.
//
// Parameters:
//     pszATResponse [in]: response string from the module
//     uiBufLen [in]: length of pszATResponse
//     pszModuleType [out]: returns the full name of the module, can be set to NULL
//     nModuleTypeBufLen [in]: length of buffer of pszModuleType
//
// Return value: type of module (enum MODEMTYPE), MODEMTYPE_UNKNOWN on error.
//
MODEMTYPE GetModemTypeFromResponse(const char *pszATResponse, UINT uiBufLen, char *pszModuleType, UINT nModuleTypeBufLen)
{
    DWORD dwStartPos, dwEndPos;
    UINT iPos = 0;
    MODEMTYPE nModemType = MODEMTYPE_UNKNOWN;

    if (pszModuleType != NULL) memset(pszModuleType, 0, nModuleTypeBufLen);
    dwStartPos = 0;
    while (g_SupportedModems[iPos].szModem != NULL)
    {
        if(FindString(dwStartPos, pszATResponse, g_SupportedModems[iPos].szModem, uiBufLen, &dwEndPos))
        {
            nModemType = g_SupportedModems[iPos].dwType;
            if (pszModuleType != NULL)
                (void)strncpyz(pszModuleType, g_SupportedModems[iPos].szModem, nModuleTypeBufLen);
            break;
        }
        iPos++;
    }
    return nModemType;
}

//
// Parses response for AT+CGMM (module type) and checks, if this module
// is supported.
//
BOOL CRilHandle::ExParseCGMMResponse(const char *pszATResponse, UINT uiBufLen) const
{
    DWORD dwOverWrite = 0;

    g_dwModemType = GetModemTypeFromResponse(pszATResponse, uiBufLen, NULL, 0);

    if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("OverwriteRadioType"), &dwOverWrite)) {
        g_dwModemType = (dwOverWrite < NUMBER_OF_SUPPORTED_MODEMTYPES ? (MODEMTYPE)dwOverWrite : g_dwModemType);
        RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: Warning: radio type set to %d\r\n"), dwOverWrite));
    }

    if (g_dwModemType == MODEMTYPE_UNKNOWN)
        RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: Unknown radio type.\r\n")));

    return (g_dwModemType == MODEMTYPE_UNKNOWN ? FALSE : TRUE);
}

//
// Parses response for AT+CGMM (module version) and checks, if this version
// is supported.
//
BOOL CRilHandle::ExParseCGMRResponse(const char *pszATResponse) const
{
    const char *ptr;
    int  iVerMin = 0;
    int  iVerMaj = 0;
    BOOL bIsValid = FALSE;
    DWORD dwOverWrite = 0;

    ptr = strstr(pszATResponse, "REVISION");
    if (ptr != NULL && strlen(ptr)>11)
    {
        iVerMaj = atoi(ptr + 9);
        ptr = strstr(ptr, ".");

        if (ptr != NULL && strlen(ptr)>1)
            iVerMin = atoi(ptr + 1);
        else
            iVerMin = 0;
    }
    g_dwModemVersion = (iVerMaj * 1000) + iVerMin;

    if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("OverwriteRadioVersion"), &dwOverWrite)) {
        g_dwModemVersion = dwOverWrite;
        RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: Warning: radio version set to %d\r\n"), dwOverWrite));
    }
    if (g_dwModemType < NUMBER_OF_SUPPORTED_MODEMTYPES)
        bIsValid = (g_dwModemVersion <= g_dwMaxModemVersion[g_dwModemType] && g_dwModemVersion >= g_dwMinModemVersion[g_dwModemType]);
    if (!bIsValid)
        RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: Invalid radio version.\r\n")));

    return bIsValid;
}
//
// Parses response of at^cicret=swn to get internal product version,
// Check if this version is valid or not.
//
BOOL CRilHandle::ExParseCICRETRResponse(const char *pszATResponse) const
{  
	const char *ptr;
    DWORD  iInternalVer = 0;
    BOOL bIsValid = FALSE;

   
    RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: Invalid line 709.\r\n")));
    ptr = strchr(pszATResponse, '_');
    if(ptr != NULL && strlen(ptr) >= 10)
    {
	    RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: Invalid line 713.\r\n")));
			
	    ptr++;
		iInternalVer = atoi(ptr); 
			
		if(!iInternalVer)
		{
			ptr = strchr(ptr, '_');
			if(ptr != NULL && strlen(ptr) >= 8)
			{
				ptr++;
				iInternalVer = atoi(ptr); 
				RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: 1st Internal product version - %d.\r\n"),iInternalVer));		
			}
		}
		    
    }
    g_dwProductInternalVersion = iInternalVer;

    bIsValid = (g_dwProductInternalVersion == 100 || g_dwProductInternalVersion == 300 || g_dwProductInternalVersion == 400 || g_dwProductInternalVersion == 500);
    if (!bIsValid)
        RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: Invalid internal product version.\r\n")));

    RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: Internal product version - %d.\r\n"),g_dwProductInternalVersion));

    return bIsValid;
}

//
// Reads modem type and version and checks, if this modem type/version is
// supported by this version of the RIL.
// The result is stored in the global variables g_dwModemType and g_dwModemVersion.
// These values are used in the feature definitions (see: modulespecifics.h).
//
// Return value: TRUE, if the modem is supported, otherwise FALSE.
//
BOOL CRilHandle::CheckModemType(COM_PORT_TYPE PortType)
{
    const char *szModemTypeCmd = "AT+CGMM\r";
    const char *szModemVerCmd = "AT+CGMR\r";
	const char *szProductInternalVerCmd = "AT^CICRET=SWN\r";
    char szResponse[MAX_DEFAULT_INIT_AT_RESPONSE];
    BOOL fRet = FALSE;

    m_fUseGMMCommand = FALSE;

    if (!ExSendATCmdGetRsp(PortType, (char*)szModemTypeCmd, szResponse, sizeof(szResponse), GetPreMuxInitTimeout(), 1))
    {
        goto Error;
    }
    // Bug in the PXS8: while in airplane mode, the PXS8 doesn't support the
    // AT+CGMM command, the identical command AT+GMM works. So, if the AT+CGMM
    // commands returns "ERROR", there is still a chance, that the AT+GMM command
    // succeeds. With the EHS5, on the other hand. AT+GMM doesn't work, only
    // AT+CGMM does, so theRIL has to try both commands.
    if (strstr(szResponse, "ERROR") != NULL) {
        szModemTypeCmd = "AT+GMM\r";
        szModemVerCmd = "AT+GMR\r";
        m_fUseGMMCommand = TRUE;
        if (!ExSendATCmdGetRsp(PortType, (char*)szModemTypeCmd, szResponse, sizeof(szResponse), GetPreMuxInitTimeout(), 1))
        {
            goto Error;
        }
    }
    if(!ExParseCGMMResponse(szResponse, sizeof(szResponse)))
    {
        RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: Unable to get modem type\r\n")));
        goto Error;
    }

    if (!ExSendATCmdGetRsp(PortType, (char*)szModemVerCmd, szResponse, sizeof(szResponse), GetPreMuxInitTimeout(), 1))
    {
        goto Error;
    }
    if(!ExParseCGMRResponse(szResponse))
    {
        RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: Unable to get modem version\r\n")));
        goto Error;
    }
	
	//ELS6x product and ELS8x product are identified in format "BOXWOOD_XX_YYY_ZZZ".
	//XX is module series, include E, US, USA, AUS
	//Y is product internal revision, include 100,300,400,500
	//ZZZ is the specific product release version such as 010 or 012 etc.
	if(SUPPORTS_INTERNAL_PRODUCT_VERSION)
	{
		if (!ExSendATCmdGetRsp(PortType, (char*)szProductInternalVerCmd, szResponse, sizeof(szResponse), GetPreMuxInitTimeout(), 1))
		{
			goto Error;
		}
		if(!ExParseCICRETRResponse(szResponse))
		{
			RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: Unable to get internal product version or the version is invalid.\r\n")));
			goto Error;
		}
	}

    if (UNSUPPORTED_MODULE)
    {
        RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: Unsupported modem type / version\r\n")));
        goto Error;
    }
#ifdef RILDRV_CE6
    if (!SUPPORTS_SLCC)
    {
        RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: Unsupported modem type (missing ^SLCC support).\r\n")));
        goto Error;
    }
#endif

    fRet = TRUE;

Error:

    return fRet;
}

//
// The timeouts of AT commands, which are used before the multiplexer is started, are not
// controlled with the usual registry keys. Instead there are three special keys:
// "EarlyInitIPR": only for AT+IPR (not used on USB)
// "EarlyInitSBNR": for AT^SBNR (used to read audio block version), also used for AT$PRL
// "EarlyInitSJDL": for AT^SJDL (used to update EHS5 Java midlet)
// "EarlyInit": deafult timeout for all other commands.
// "EarlyInitSBNW": for AT^SBNW (used to delete blocks)
//
// Return value: timeout in ms.
//
DWORD CRilHandle::GetPreMuxInitTimeout(PREMUX_TIMEOUTS iTimeout) const
{
    DWORD dwTimeouts[] = {5000, 20000, 1000, 30000, 60000};
    DWORD dwTimeout = 0;
    const TCHAR *tszRegKey[] = {TEXT("EarlyInit"), TEXT("EarlyInitSBNR"), TEXT("EarlyInitIPR"), TEXT("EarlyInitSJDL"), TEXT("EarlyInitSBNW")};

    (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, tszRegKey[iTimeout], &dwTimeout);

    if (dwTimeout == 0)
        dwTimeout = dwTimeouts[iTimeout];

    return dwTimeout;
}

//
// For the GPRS attach mode there are two relevant registry keys, which are read here:
// "EnableGPRSAttach": determines, if the automatic GPRS attach by the module should be used.
// "EnableGPRSReAttach": determines, if the RIL should force a re-attach in case the attach is lost.
// The value are stored in the global variables g_dwEnableGPRSAttach and g_dwEnableGPRSReAttach.
//
// Return value: FALSE on any fatal error, otherwise TRUE.
//
BOOL CRilHandle::InitGPRSAttachMode(COM_PORT_TYPE PortType, BOOL *pbNeedRestart)
{
    DWORD dwAttach;

    if (NO_SUPPORT_GPRS_AUTOATTACH_CFG)
        return TRUE;

    dwAttach = 0;
    if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyIntrinsycRIL, TEXT("EnableGPRSAttach"), &dwAttach) && GPRS_AUTO_ATTACH_CONFIGURATION)
    {
        char szNewSetting[MAX_ATCMD_LEN];

        g_dwEnableGPRSAttach = dwAttach;
        g_dwEnableGPRSReAttach = 0;
        (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyIntrinsycRIL, TEXT("EnableGPRSReAttach"), &g_dwEnableGPRSReAttach);
        (void)_snprintfz(szNewSetting, sizeof(szNewSetting), "\"%s\"", dwAttach==1?"enabled":"disabled");
        if (!CheckSCFGSetting(PortType, "GPRS/AutoAttach", szNewSetting, NULL, pbNeedRestart, 0))
            return FALSE;
    }
    else {
        g_dwEnableGPRSAttach = 0;
        g_dwEnableGPRSReAttach = 0;
    }
    return TRUE;
}

//
// Shutdown module. This function is supposed to be called only
// during the pre-mux-init phase, after the radio configuration
// has been changed.
//
void CRilHandle::ShutdownOnConfigChange(COM_PORT_TYPE PortType)
{
    DWORD dwTimeout;
    char szResponse[MAX_ATCMD_LEN];

    dwTimeout = GetPreMuxInitTimeout();
    memset(szResponse, 0, sizeof(szResponse));
    // Send "AT^SMSO" and ignore response, the module is going to be turned off anyway.
#ifdef SUPPORTS_EHSX_R1
    if (NO_SUPPORT_SMSO)
        (void)ExSendATCmdGetRsp(PortType, "AT+CPWROFF\r", szResponse, sizeof(szResponse), dwTimeout, 0);
    else
#endif
        (void)ExSendATCmdGetRsp(PortType, "AT^SMSO\r", szResponse, sizeof(szResponse), dwTimeout, 0);
    // Wait a short time, while the module is writing the configuration.
    Sleep(WAIT_FOR_CONFIG_WRITE);
    return;
}

//
// This function checks, if the module has got a valid IMEI.
// If there is no valid IMEI, the function checks, if there is a customer IMEI configured
// via registry. If so, if writes the customer IMEI.
// Additionally to the Customer IMEI this function also checks if a specific SVN is configured
// in the registry and sets it accordingly.
// After changing the IMEI or SVN the function sets pbNeedRestart to TRUE to initiate a
// restart to make the changes effective.
//
// Parameter: pbNeedRestart [out]: is set to TRUE if a restart of the module is now necessary
//
// Return: TRUE: valid IMEI is already present, no further action necessary.
//         FALSE: shutdown necessary, either because of an error (pbNeedRestart == FALSE), or if restart is needed (pbNeedRestart == TRUE)
//
BOOL CRilHandle::TestForValidIMEI(COM_PORT_TYPE PortType, BOOL *pbNeedRestart)
{
    DWORD dwTimeout;
    char szCmd[MAX_ATCMD_LEN];
    char szResponse[MAX_ATCMD_LEN];
    BOOL bNoIMEI = FALSE;
    BOOL bNeedShutdown = FALSE;
    DWORD dwSVN = 0;

    *pbNeedRestart = FALSE;
    dwTimeout = GetPreMuxInitTimeout();

    // Step 1: check, if an IMEI is present
    memset(szResponse, 0, sizeof(szResponse));
    (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CGSN\r");
    if (!ExSendATCmdGetRsp(PortType, szCmd, szResponse, sizeof(szResponse), dwTimeout, 0)) {
      return FALSE;
    }
    if (strstr(szResponse, "+CME ERROR: 23") != NULL) {
        WCHAR wszCustomerIMEI[MAX_ATCMD_LEN];

        memset(wszCustomerIMEI, 0, sizeof(wszCustomerIMEI));
        // If the module has no valid IMEI, it must not be used (3GPP requirement).
        // But it is possible, that there is a customer IMEI to be set.
        bNoIMEI = TRUE;
        bNeedShutdown = TRUE;
        if (GetRegistrySZ(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("ConfigureIMEI"), wszCustomerIMEI, MAX_ATCMD_LEN)) {
            (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT^SGSN=\"%S\"\r", wszCustomerIMEI);
            if (!ExSendATCmdGetRsp(PortType, szCmd, szResponse, sizeof(szResponse), dwTimeout, 0)) {
              return FALSE;
            }
            // If command succeeded, the IMEI is written and a restart is necessary.
            if (strstr(szResponse, "OK\r\n") != NULL) {
                *pbNeedRestart = TRUE;
                bNoIMEI = FALSE;
            }
        }
    }

    // Step 2: Check SVN
    if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("ConfigureSVN"), &dwSVN) && !bNoIMEI) {
        const char *pszSVNRspPrefix = "^SSVN: ";
        char *pszRsp;
        DWORD dwCurrentSVN = dwSVN;

        // Check, if the current SVN. this command may fail, if this module doesn't allow
        // to set the C-IMEI or SVN.
        memset(szResponse, 0, MAX_ATCMD_LEN);
        if (!ExSendATCmdGetRsp(PortType, "AT^SSVN?\r", szResponse, MAX_ATCMD_LEN, dwTimeout, 0)) {
            return FALSE;
        }
        pszRsp = strstr(szResponse, pszSVNRspPrefix);
        if (pszRsp != NULL)
            dwCurrentSVN = (UINT)atoi(pszRsp+strlen(pszSVNRspPrefix));
        if (dwCurrentSVN != dwSVN) {
            (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT^SSVN=%d\r", dwSVN);
            if (!ExSendATCmdGetRsp(PortType, szCmd, szResponse, sizeof(szResponse), dwTimeout, 0)) {
              return FALSE;
            }
            // If command succeeded, the SVN is written and a restart is necessary.
            if (strstr(szResponse, "OK\r\n") != NULL) {
                *pbNeedRestart = TRUE;
                bNeedShutdown = TRUE;
            }
        }
    }

    // Step 3: If this module ha no valid IMEI and it also not possible to configure a
    // customer IMEI, then this module is not allowed in the public networks. In this
    // case the RIL will shut down this module.
    if (bNoIMEI) {
        RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: No valid IMEI.\r\n")));
#ifndef ALLOW_NOIMEI_MODULES
        bNeedShutdown = TRUE;
#ifdef RIL_INDICATE_NO_IMEI
        // Optionally the RIL can send a notification about this forced shutdown.
        INT cbStruct;
        RILSUPSERVICEDATA *prssd;
        WCHAR wszText[MAX_RESOURCE_STRING_LEN];
        CNotificationData* pnd2 = NULL;

        if (LoadString((HINSTANCE)hInstance, IDS_INDICATION_NOIMEI, wszText, MAX_RESOURCE_STRING_LEN) > 0)
        {
            cbStruct = sizeof(RILSUPSERVICEDATA) + (wcslen(wszText) * sizeof(WCHAR)) + sizeof(WCHAR);
            prssd = (RILSUPSERVICEDATA*)AllocBlob(cbStruct);

            if (prssd != NULL)
            {
                memcpy(prssd->pbData, (BYTE *)wszText, (wcslen(wszText) * sizeof(WCHAR)) + sizeof(WCHAR));
                prssd->dwParams = RIL_PARAM_SSDI_DATA;
                prssd->dwStatus = RIL_SUPSVCDATASTATUS_UNSUPPORTED;
                prssd->dwParams |= RIL_PARAM_SSDI_STATUS;
                prssd->cbSize = cbStruct;
                pnd2 = new CNotificationData;
                if (pnd2)
                {
                    if (pnd2->InitFromRealBlob(RIL_NOTIFY_SUPSERVICEDATA, (void*)prssd, prssd->cbSize))
                        BroadcastNotification(pnd2);
                    else
                        delete pnd2;
                }
                FreeBlob(prssd);
            }
        }
#endif
#else
        bNeedShutdown = FALSE;
#endif
    }
    if (bNeedShutdown) {
        // Shut down module immediately, to prevent it from accidently register to the network.
#ifdef SUPPORTS_EHSX_R1
        if (NO_SUPPORT_SMSO)
            (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CPWROFF\r");
        else
#endif
            (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT^SMSO\r");
        if (MISSING_OK_ON_SMSO)
            (void)ExSendATCommand(PortType, szCmd);
        else
            (void)ExSendATCmdGetRsp(PortType, szCmd, szResponse, sizeof(szResponse), dwTimeout, 0);
        return FALSE;
    }
    return TRUE;
}

//
// This function sets some advanced configurations of the radio:
// - Fast registration mode (HC25/HC28)
// - UDI mode (HC25/HC28)
// - Sync PPP mode (HC25/HC28)
// - VExt power mode (PH8)
// - Maximum HSDPA and HSUPA data rate (PH8)
// - Antenna diversity (PH8)
// - EDGE/GPRS multislot class (PH8)
//
// Parameter:
//    pbNeedRestart [out]: will be set to TRUE, if a setting has been changed, which requires a restart
//
// Return value: FALSE if dsome fatal error occurred, otherwise OK.
//
BOOL CRilHandle::InitConfigurationSettings(COM_PORT_TYPE PortType, BOOL *pbNeedRestart)
{
    char szNewSetting[SCFG_MAX_SETTING_LEN];
    WCHAR wszCmd[MAX_ATCMD_LEN];
    DWORD dwMode = 0;
    BOOL fSendCommand = TRUE;

    dwMode = 0;
    if (!GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("EnableFastRegistrationMode"), &dwMode))
    {
        // For the HC28 the fast registration mode is to be enabled per default,
        // otherwise according to registry key.
        if (FAST_REGISTRATION_AS_DEFAULT)
            dwMode = 1;
        else
            fSendCommand = FALSE;
    }
    if (SUPPORTS_FASTRM_CONFIG && fSendCommand)
    {
        (void)_snprintfz(szNewSetting, sizeof(szNewSetting), "\"%s\"", dwMode==1?"1":"0");
        if (!CheckSCFGSetting(PortType, "MEopMode/RM", szNewSetting, NULL, pbNeedRestart, 0))
            return FALSE;
    }

    if (SUPPORTS_UDI_CONFIG)
    {
        // The following keys are usually only relevant when using UDI in Japan.
        dwMode = 0;
        fSendCommand = TRUE;
        if (!GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("ConfigureUDIMode"), &dwMode))
        {
            // For the HC28 the special UDI mode is to be enabled per default,
            // otherwise according to registry key.
            if (SPECIAL_UDI_CONFIGURATION)
                dwMode = 1;
            else
                fSendCommand = FALSE;
        }
        if (fSendCommand)
        {
            (void)_snprintfz(szNewSetting, sizeof(szNewSetting), "\"%s\"", dwMode==1?"1":"0");
            if (!CheckSCFGSetting(PortType, "Call/UDI", szNewSetting, NULL, pbNeedRestart, 0))
                return FALSE;
        }
    }

    if (SUPPORTS_SYNCPPP_CONFIG)
    {
        dwMode = 0;
        fSendCommand = TRUE;
        if (!GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("ConfigureSyncPPPMode"), &dwMode))
        {
            // For the HC28 the SyncPPP mode is to be enabled per default,
            // otherwise according to registry key.
            if (SPECIAL_UDI_CONFIGURATION)
                dwMode = 1;
            else
                fSendCommand = FALSE;
        }
        if (fSendCommand)
        {
            (void)_snprintfz(szNewSetting, sizeof(szNewSetting), "\"%s\"", dwMode==1?"1":"0");
            if (!CheckSCFGSetting(PortType, "Call/SyncPPP", szNewSetting, NULL, pbNeedRestart, 0))
                return FALSE;
        }
    }

    if (SUPPORTS_VEXT_POWER_MODE)
    {
        dwMode = 0;
        (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("VExtPowerMode"), &dwMode);
        (void)_snprintfz(szNewSetting, sizeof(szNewSetting), "\"%s\"", dwMode==1?"high":"low");
        if (!CheckSCFGSetting(PortType, "MEopMode/PowerMgmt/VExt", szNewSetting, NULL, pbNeedRestart, 0))
            return FALSE;
    }

    if (SUPPORTS_CONFIG_HSDPA_DATA_RATE)
    {
        // Valid values for HSDPA data rate:
        // 0: 14.4 MBit/s
        // 1: 7.2 MBit/s
        dwMode = 0;
        (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("MaxHSDPADataRate"), &dwMode);
        (void)_snprintfz(szNewSetting, sizeof(szNewSetting), "\"%s\"", dwMode==1?"1":"0");
        if (!CheckSCFGSetting(PortType, "GPRS/MaxDataRate/HSDPA", szNewSetting, NULL, pbNeedRestart, 0))
            return FALSE;
    }

    if (SUPPORTS_CONFIG_HSUPA_DATA_RATE)
    {
        // Valid values HSUPA data rate:
        // 0: 5.76 MBit/s
        // 1: 2 MBit/s
        dwMode = 0;
        (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("MaxHSUPADataRate"), &dwMode);
        (void)_snprintfz(szNewSetting, sizeof(szNewSetting), "\"%s\"", dwMode==1?"1":"0");
        if (!CheckSCFGSetting(PortType, "GPRS/MaxDataRate/HSUPA", szNewSetting, NULL, pbNeedRestart, 0))
            return FALSE;
    }

    if (SUPPORTS_ANTENNA_DIVERSITY)
    {
        char szCmd[MAX_ATCMD_LEN];
        char szResponse[MAX_ATCMD_LEN];

        // Antenna diversity, possible values:
        // 0: off (1 antenna)
        // 1: on (2 antennas)
        // 3: test mode
        dwMode = 0;
        if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("AntennaDiversity"), &dwMode))
        {
            DWORD dwTimeout;
            const char *pszSADRspPrefix = "^SAD: ";
            char *pszRsp;
            DWORD dwCurrentSAD = dwMode;

            dwTimeout = GetPreMuxInitTimeout();
            memset(szResponse, 0, MAX_ATCMD_LEN);
            if (!ExSendATCmdGetRsp(PortType, "AT^SAD=12\r", szResponse, MAX_ATCMD_LEN, dwTimeout, 0)) {
                return FALSE;
            }

            pszRsp = strstr(szResponse, pszSADRspPrefix);
            if (pszRsp != NULL)
                dwCurrentSAD = (UINT)atoi(pszRsp+strlen(pszSADRspPrefix));
            if (dwCurrentSAD >= 10)
                dwCurrentSAD = dwCurrentSAD - 10;

           if (dwCurrentSAD != dwMode) {
              (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT^SAD=1%d\r", dwMode);
              if (!ExSendATCmdGetRsp(PortType, szCmd, szResponse, sizeof(szResponse), dwTimeout, 0)) {
                return FALSE;
              }
              if (pbNeedRestart != NULL)
                *pbNeedRestart = TRUE;
           }
        }
    }

    if (SUPPORTS_CONFIG_GPRSEDGE_CLASS) {
        char szCmd[MAX_ATCMD_LEN];
        char szResponse[MAX_ATCMD_LEN];
        DWORD dwMSClass = 0;
        DWORD dwMSClassEDGE = 0;

        // GPRS/EDGE multislot setting:
        // DefaultMSClass: GPRS multislot class
        // DefaultMSClassEDGE: EDGE multislot class
        // If DefaultMSClassEDGE is not set, the GPRS MS class will be used for EDGE as well.
        if (!GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("DefaultMSClass"), &dwMSClass))
            dwMSClass = 0;

        if (!GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("DefaultMSClassEDGE"), &dwMSClassEDGE))
            dwMSClassEDGE = dwMSClass;      // As default use same MS class for GPRS and EDGE

        if (dwMSClass > 0) {
            const char *pszSGCONFRspPrefix = "^SGCONF: ";
            char *pszRsp;
            DWORD dwTimeout;
            DWORD dwCurrentMSClass = dwMSClass;
            DWORD dwCurrentMSClassEDGE = dwMSClassEDGE;

            dwTimeout = GetPreMuxInitTimeout();
            memset(szResponse, 0, MAX_ATCMD_LEN);
            if (!ExSendATCmdGetRsp(PortType, "AT^SGCONF?\r", szResponse, MAX_ATCMD_LEN, dwTimeout, 0)) {
                return FALSE;
            }
            pszRsp = strstr(szResponse, pszSGCONFRspPrefix);
            if (pszRsp != NULL) {
                pszRsp = strstr(pszRsp, ",");
                if (pszRsp != NULL)
                    pszRsp = strstr(pszRsp+1, ",");
                if (pszRsp != NULL) {
                    dwCurrentMSClass = (UINT)atoi(pszRsp + 1);
                    pszRsp = strstr(pszRsp+1, ",");
                }
                if (pszRsp != NULL)
                    dwCurrentMSClassEDGE = (UINT)atoi(pszRsp + 1);
            }

            if (dwCurrentMSClass != dwMSClass || dwCurrentMSClassEDGE != dwMSClassEDGE) {
                (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT^SGCONF=,,%d,%d\r", dwMSClass, dwMSClassEDGE);
                if (!ExSendATCmdGetRsp(PortType, szCmd, szResponse, sizeof(szResponse), dwTimeout, 0)) {
                  return FALSE;
                }
                if (pbNeedRestart != NULL)
                  *pbNeedRestart = TRUE;
            }
        }
    }

    if (SUPPORTS_ADDITIONAL_RADIO_CFG) {
        dwMode = 0;
        if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("ConfigureRadioNWSM"), &dwMode))
        {
            (void)_snprintfz(szNewSetting, sizeof(szNewSetting), "\"%d\"", dwMode);
            if (!CheckSCFGSetting(PortType, "Radio/NWSM", szNewSetting, NULL, NULL, 0))
                return FALSE;
        }
        dwMode = 0;
        if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("ConfigureRadioOutputPower"), &dwMode))
        {
            (void)_snprintfz(szNewSetting, sizeof(szNewSetting), "\"%d\"", dwMode);
            if (!CheckSCFGSetting(PortType, "Radio/OutputPowerReduction", szNewSetting, NULL, pbNeedRestart, 0))
                return FALSE;
        }
        dwMode = 0;
        if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("ConfigureSecurityGEA"), &dwMode))
        {
            (void)_snprintfz(szNewSetting, sizeof(szNewSetting), "\"%d\"", dwMode);
            if (!CheckSCFGSetting(PortType, "Security/GEA", szNewSetting, NULL, pbNeedRestart, 0))
                return FALSE;
        }
    }

    if (SUPPORTS_CDMA_TETHERED_NAI) {
        dwMode = 1;
        if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("EnableCdmaTetheredNai"), &dwMode))
        {
            (void)_snprintfz(szNewSetting, sizeof(szNewSetting), "\"%s\"", dwMode == 1 ? "enabled" : "disabled");
            if (!CheckSCFGSetting(PortType, "CDMA/TetheredNai", szNewSetting, NULL, NULL, 0))
                return FALSE;
        }
    }

    // Global mode modules can operate as CDMA only, UMTS only and in multimode.
    // Possible settings: 0:multimode/1:cdma/2:umts
    if (GLOBAL_MODE_RADIO)
    {
        dwMode = 0;
        (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("GlobalModeConfig"), &dwMode);
        m_nMultiModeSetting = (MULTIMODE_CONFIG)dwMode;
        switch (m_nMultiModeSetting) {
            case MULTIMODE_CONFIG_CDMA:
                if (OLD_MULTIMODE_SETTINGS)
                    (void)_snprintfz(szNewSetting, sizeof(szNewSetting), "\"%s\"", "cdma");
                else
                    (void)_snprintfz(szNewSetting, sizeof(szNewSetting), "\"%s\"", "cdma2000");
                break;
            case TEC_MODE_LTE:
                if (OLD_MULTIMODE_SETTINGS)
                    (void)_snprintfz(szNewSetting, sizeof(szNewSetting), "\"%s\"", "umts");
                else
                    (void)_snprintfz(szNewSetting, sizeof(szNewSetting), "\"%s\"", "GSM/UMTS");
                break;
            default:
                if (OLD_MULTIMODE_SETTINGS)
                    (void)_snprintfz(szNewSetting, sizeof(szNewSetting), "\"%s\"", "multimode");
                else
                    (void)_snprintfz(szNewSetting, sizeof(szNewSetting), "\"%s\"", "MULTIMODE");
        }
        if (!CheckSCFGSetting(PortType, "MEopMode/AccT", szNewSetting, NULL, pbNeedRestart, 0))
            return FALSE;
    }

	// EHSx: configure the serial interface
    if (CONFIG_SERIAL_IF_REQUIRED)
    {
        dwMode = 1;  // Recommended default setting for the EHS5.
        (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("DefaultSerialConfig"), &dwMode);
        (void)_snprintfz(szNewSetting, sizeof(szNewSetting), "\"%d\"", dwMode);
        if (!CheckSCFGSetting(PortType, "Serial/Interface/Allocation", szNewSetting, NULL, pbNeedRestart, 0))
            return FALSE;
    }
	
    // EHSx: configure the GPIO settings.
    if (SUPPORTS_GPIO_CONFIG)
    {
        WCHAR wszGPIOkey[SCFG_MAX_SETTING_LEN];
        char szGPIOsetting[SCFG_MAX_SETTING_LEN];
        const char *szGPIOsettings[] = {"ASC1", "DAI", "DCD0", "DSR0", "DTR0", "FSR", "PULSE", "PWM", "RING0", "SPI", "SYNC", "GPSANT", NULL};
        INT iGPIO = 0;

        while (szGPIOsettings[iGPIO] != NULL) {
            (void)_snwprintfz(wszGPIOkey, SCFG_MAX_SETTING_LEN, L"GpioConfig%S", szGPIOsettings[iGPIO]);
            dwMode = 0;
            if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, wszGPIOkey, &dwMode)) {
                if (dwMode == 0)
                    (void)_snprintfz(szNewSetting, sizeof(szNewSetting), "\"%s\"", "std");
                else
                    (void)_snprintfz(szNewSetting, sizeof(szNewSetting), "\"%s\"", "gpio");
                (void)_snprintfz(szGPIOsetting, sizeof(szGPIOsetting), "Gpio/mode/%s", szGPIOsettings[iGPIO]);
                if (!CheckSCFGSetting(PortType, szGPIOsetting, szNewSetting, NULL, pbNeedRestart, 0))
                    return FALSE;
            }
            iGPIO++;
        }
    }

    // The GPS engine (if available) may need some configuration as well.
    if (!g_GpsHandling.ConfigureGpsSettings(pbNeedRestart)){
        RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: Config GPS settings failed, *pbNeedRestart = (%d)\r\n"), *pbNeedRestart));
        return FALSE;
    }

    // Optional customer defined AT command to be sent only once.
    if (GetRegistrySZ(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("EarlyPreInitCommandOnce"), wszCmd, MAX_ATCMD_LEN)) {
        char szCmd[MAX_ATCMD_LEN];
        char szResponse[MAX_ATCMD_LEN];
        DWORD dwTimeout;

        if (wcslen(wszCmd) > 0) {
            RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: Send One-Time AT initialization command.\r\n")));
            dwTimeout = GetPreMuxInitTimeout();
            (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "%S\r", wszCmd);
            if (!ExSendATCmdGetRsp(PortType, szCmd, szResponse, sizeof(szResponse), dwTimeout, 0)) {
              return FALSE;
            }
            // Command has been been executed by the module, the registry key can now be deleted.
            (void)SetRegistrySZ(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("EarlyPreInitCommandOnce"), L"");
        }
    }

    // Optional customer defined AT command
    if (GetRegistrySZ(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("EarlyPreInitCommand"), wszCmd, MAX_ATCMD_LEN)) {
        char szCmd[MAX_ATCMD_LEN];
        char szResponse[MAX_ATCMD_LEN];
        DWORD dwTimeout;

        dwTimeout = GetPreMuxInitTimeout();
        (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "%S\r", wszCmd);
        if (!ExSendATCmdGetRsp(PortType, szCmd, szResponse, sizeof(szResponse), dwTimeout, 0)) {
          return FALSE;
        }
    }
    return TRUE;
}

//
// Reads the current ECC (emergency numbers, if EF_ECC SIM file isn't available).
// Sets new setting, if configured via registry.
//
BOOL CRilHandle::CheckECCSettings(COM_PORT_TYPE PortType, BOOL *pbNeedRestart)
{
    char szResponse[MAX_ATCMD_LEN];
    char szNewSetting[10];

    g_uiECCModule = 39;     // Default ECC numbers: 000, 08, 110, 999

    memset(szResponse, 0, MAX_ATCMD_LEN);
    if (SCFG_ECC_HANDLING_AVAILABE) {
        DWORD dwECCNumbers = 0;
        BOOL bECCConfigAvailable = FALSE;
        const char *szSCFGconfig = "Call/ECC";

        bECCConfigAvailable = GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("SetRadioECCNumbers"), &dwECCNumbers);
        (void)_snprintfz(szNewSetting, sizeof(szNewSetting), "\"%d\"", dwECCNumbers);
        if (!CheckSCFGSetting(PortType, szSCFGconfig, bECCConfigAvailable ? szNewSetting : NULL, szResponse, pbNeedRestart, 0))
            return FALSE;

        // The new setting will become active after the next reboot, so the old setting must still be used for this session.
        g_uiECCModule = (UINT)atoi(szResponse);
    }

    return TRUE;
}

//
// This function checks the current SDPORT setting. If the current setting is
// not the one defined in the reigstry, the function will set the correct one.
//
// Return value: TRUE, if SDPORT has been changed an a new start
//       of the USB driver is necessary. Otherwise FALSE.
//
BOOL CRilHandle::EnsureSDPORTsetting(COM_PORT_TYPE PortType)
{
    DWORD dwTimeout;
    DWORD dwSDPORT = 3;
    char szResponse[MAX_ATCMD_LEN];
    BOOL bNeedRestart = FALSE;

    if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("ConfigureSDPORT"), &dwSDPORT)) {
        char szCmd[MAX_ATCMD_LEN];
        const char *pszSDPortRspPrefix = "^SDPORT: ";
        char *pszRsp;
        DWORD dwCurrentSDPORT = dwSDPORT;

        dwTimeout = GetPreMuxInitTimeout();
        memset(szResponse, 0, MAX_ATCMD_LEN);
        if (!ExSendATCmdGetRsp(PortType, "AT^SDPORT?\r", szResponse, MAX_ATCMD_LEN, dwTimeout, 0)) {
            goto Error;
        }
        pszRsp = strstr(szResponse, pszSDPortRspPrefix);
        if (pszRsp != NULL)
            dwCurrentSDPORT = (UINT)atoi(pszRsp+strlen(pszSDPortRspPrefix));

        if (dwCurrentSDPORT != dwSDPORT) {
            dwTimeout = GetPreMuxInitTimeout();
            memset(szResponse, 0, MAX_ATCMD_LEN);
            (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT^SDPORT=%d\r", dwSDPORT);
            if (!ExSendATCmdGetRsp(PortType, szCmd, szResponse, sizeof(szResponse), dwTimeout, 0))
                goto Error;
            bNeedRestart = TRUE;
        }
    }

Error:
    return bNeedRestart;
}

//
// Checks an AT^SCFG or AT^SGPSC configuration, and if it is different from the required
// value changes it.
//
// Parameters:
//    PortType [in]: port (data/URC) where the AT command should be send
//    szConfiguration [in]: AT^SCFG configuration (eg. "Call/ECC") without quotation marks
//    szSetting [in] (optional): Required value for the setting including quotation marks, if omitted, the RIL will not change the current setting
//    szOriginal [out] (optional): contains the original setting (buffer length will be assumed to be MAX_ATCMD_LEN)
//    pfChanged [out] (optional): will be set to TRUE, if the value had been changed, otherwise the value won't be changed
//    iCmdType [in]: type of command: 0 = AT^SCFG, 1 = AT^SPGSC
//
BOOL CRilHandle::CheckSCFGSetting(COM_PORT_TYPE PortType, const char *szConfiguration, char *szSetting, char *szOriginal, BOOL *pfChanged, INT iCmdType)
{
    const char *cszCmd = "^SCFG";
    DWORD dwTimeout;
    char szCmd[MAX_ATCMD_LEN];
    char szResponse[MAX_ATCMD_LEN];
    char szResponsePrefix[MAX_ATCMD_LEN];
    char *pszRsp;
    BOOL bSuccess = TRUE;

    // Type 1: AT^SPGSC instead of AT^SCFG
    if (iCmdType == 1)
        cszCmd = "^SGPSC";

    (void)_snprintfz(szResponsePrefix, MAX_ATCMD_LEN, "\"%s\",", szConfiguration);
    dwTimeout = GetPreMuxInitTimeout();
    memset(szResponse, 0, MAX_ATCMD_LEN);

    // Query current configuration value
    (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT%s=\"%s\"\r", cszCmd, szConfiguration);
    if (!ExSendATCmdGetRsp(PortType, szCmd, szResponse, MAX_ATCMD_LEN, dwTimeout, 0)) {
        bSuccess = FALSE;
        goto Error;
    }
    pszRsp = strstr(szResponse, szResponsePrefix);
    if (pszRsp != NULL) {
        // Store original value (if required)
        if (szOriginal != NULL) {
            char *pTmp;

            // Remove prefix
            (void)strncpyz(szOriginal, pszRsp + strlen(szConfiguration) + 3, MAX_ATCMD_LEN);
            // Remove "OK"
            pTmp = strstr(szOriginal, "\r");
            if (pTmp != NULL) *pTmp = '\0';
            // Remove qutoation marks
            if (szOriginal[0] == '\"') memmove(szOriginal, szOriginal+1, strlen(szOriginal));
            if (strlen(szOriginal) > 0 && szOriginal[strlen(szOriginal)-1] == '\"') szOriginal[strlen(szOriginal)-1] = '\0';
        }
        // New value given? Compare with current value and change, if needed
        if (szSetting != NULL) {
            // Compare values
            if (strncmp(pszRsp+strlen(szResponsePrefix), szSetting, strlen(szSetting)) != 0) {
                DWORD nRetries = 5;
                DWORD nDelay = 1000;

                GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("SCFGInitRetries"), &nRetries);
                GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("SCFGInitRetryDelay"), &nDelay);
                nRetries++;
                // Must change setting, generate AT command
                (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT%s=\"%s\",%s\r", cszCmd, szConfiguration, szSetting);
                bSuccess = FALSE;
                while (nRetries > 0) {
                    memset(szResponse, 0, MAX_ATCMD_LEN);
                    if (ExSendATCmdGetRsp(PortType, szCmd, szResponse, MAX_ATCMD_LEN, dwTimeout, 0)) {
                        if (strstr(szResponse, "ERROR") == NULL) {
                            bSuccess = TRUE;
                            break;
                        }
                    }
                    if (strstr(szResponse, "CME ERROR: 100") == NULL)
                        break;
                    Sleep(nDelay);
                    nRetries--;
                }
                if (!bSuccess)
                    goto Error;
                if (pfChanged != NULL)
                    *pfChanged = TRUE;
            }
        }
    }

Error:
    return bSuccess;
}

//
// This function tests, if the inserted SIM is an UMTS SIM.
// The detected status is stored in the m_SIMType member varaiable.
// Possible states:
// SIMTYPE_3G_SIM: USIM
// SIMTYPE_2G_SIM: SIM
//
// Return value: FALSE on fatal error, otherwise TRUE.
//
BOOL CRilHandle::TestUSIMInserted()
{
    DWORD dwTimeout, dwTemp;
    char szResponse[MAX_ATCMD_LEN];

    dwTemp = 0;
    (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("DisableDelayCSIMCommand"), &dwTemp);
    if (dwTemp == 0)
        return TRUE;

    dwTimeout = GetPreMuxInitTimeout();
    memset(szResponse, 0, MAX_ATCMD_LEN);

    if (USE_CRSM242_FOR_SIM_CHECK) {
        // If this command returns response beginning with '62', the SIM
        // currently inserted is an UMTS SIM.
        if (!ExSendATCmdGetRsp(DATA_PORT, "AT+CRSM=242\r", szResponse, sizeof(szResponse), dwTimeout, 0)) {
          return FALSE;
        }
        if (strstr(szResponse, ",\"62") != NULL)
            SetCurrentSIMType(SIMTYPE_3G_SIM);
        else
            SetCurrentSIMType(SIMTYPE_2G_SIM);
    }
    else {
        // If this command returns an error code beginning with '63', the SIM
        // currently inserted is an UMTS SIM.
        if (!ExSendATCmdGetRsp(DATA_PORT, "AT+CSIM=10,\"0020000100\"\r", szResponse, sizeof(szResponse), dwTimeout, 0)) {
          return FALSE;
        }
        if (strstr(szResponse, "+CSIM: 4,\"63") != NULL)
            SetCurrentSIMType(SIMTYPE_3G_SIM);
        else
            SetCurrentSIMType(SIMTYPE_2G_SIM);
    }

    return TRUE;
}

// For PreFast compliance: capsulation of registry command needed.
LONG MyRegCreateKeyEx(HKEY hKey, LPCWSTR lpSubKey, DWORD Reserved, LPWSTR lpClass, DWORD dwOptions, REGSAM samDesired, LPSECURITY_ATTRIBUTES lpSecurityAttributes, PHKEY phkResult, LPDWORD lpdwDisposition)
{
    return RegCreateKeyEx(hKey, lpSubKey, Reserved, lpClass, dwOptions, samDesired, lpSecurityAttributes, phkResult,  lpdwDisposition);
}

//
// Reads the networks, which are not supposed to be indicated as roaming networks from the registry.
// Registry key "NoRoamingNetworks" is a multi_sz value, where the numeric network names are
// listed. These names must not contein a space beween MCC and MNC.
// The constant MAX_NUMBER_OF_NOROAMINGNETWORKS defines how many operator at max this
// list may contain.
//
void CRilHandle::ReadNoRoamingNetworks()
{
    DWORD dwDisposition;
    HKEY hKey;

    m_fNoRomingIndicator = FALSE;
    if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("NoRoamingIndicator"), &dwDisposition))
        m_fNoRomingIndicator = (dwDisposition == 1 ? TRUE : FALSE);

    m_dwNumNoRomingNetworks = 0;
    if(MyRegCreateKeyEx(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, 0, TEXT(""), 0, KEY_READ, NULL, &hKey, &dwDisposition) == ERROR_SUCCESS)
    {
        DWORD dwSizeReturned = ALLOC_SIZE*sizeof(TCHAR);
        TCHAR szBuffer[ALLOC_SIZE];

        if (RegQueryValueEx(hKey, TEXT("NoRoamingNetworks"), 0, 0, (BYTE*)szBuffer, &dwSizeReturned) == ERROR_SUCCESS)
        {
            TCHAR *pszTmp = szBuffer;

            while (dwSizeReturned > 0 && dwSizeReturned < ALLOC_SIZE && _tcslen(pszTmp) > 0 && m_dwNumNoRomingNetworks < MAX_NUMBER_OF_NOROAMINGNETWORKS)
            {
                DWORD dwOperator;

                dwOperator = _ttoi(pszTmp);
                if (dwOperator > 0 && dwOperator <= 999999)
                {
                    m_dwNoRomingNetworks[m_dwNumNoRomingNetworks] = dwOperator;
                    m_dwNumNoRomingNetworks++;
                }
                pszTmp += _tcslen(pszTmp)+1;
            }
        }
        RegCloseKey(hKey);
    }
}

//
// Tests, if the given operator is one of those, which are not supposed to be shown as roaming.
// The list of these operators was ready at startup from the registry in function ReadNoRoamingNetworks().
//
// Parameter: dwOperator [in]: numeric network code to be searched in the list
//
// Return value: TRUE, if oberator has been found in the list.
//
BOOL CRilHandle::IsNoRoamingNetwork(DWORD dwOperator) const
{
    BOOL bIsNoRoamingNetwork = FALSE;
    UINT i;

    for (i = 0; i < m_dwNumNoRomingNetworks; i++)
    {
        if (m_dwNoRomingNetworks[i] == dwOperator)
        {
            bIsNoRoamingNetwork = TRUE;
            break;
        }
    }

    return bIsNoRoamingNetwork;
}

//
// Parsing function of CRSM-response for ENS. See CheckENSSetting() for more information.
//
static HRESULT ParseCheckENSSetting(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    UINT nSW1;
    UINT nSW2;
    LPSTR szResponseString = NULL;
    UINT cbResponseString;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    // Parse "<prefix>+CRSM: <sw1>,<sw2>"
    if (!ParseRspPrefix(szRsp, szRsp)                  ||
        !MatchStringBeginning(szRsp, "+CRSM: ", szRsp) ||
        !ParseUInt(szRsp, TRUE, nSW1, szRsp)           ||
        !MatchStringBeginning(szRsp, ",", szRsp)       ||
        !ParseUInt(szRsp, TRUE, nSW2, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Parse ","
    if (!MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseUnlimitedString(szRsp, szResponseString, cbResponseString, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    if (cbResponseString < 40) {
        hr = E_FAIL;
        goto Error;
    }
    if (pParam != NULL) {
        DWORD *pdwENS;

        pdwENS = (DWORD *)pParam;
        if (SemiByteCharsToByte(0, szResponseString[38]) & 8)
            *pdwENS = (DWORD)ENS_MENU_ENABLED;
        else
            *pdwENS = (DWORD)ENS_MENU_DISABLED;
    }
Error:
    delete[] szResponseString;
    return hr;
}

//
// This function checks the enhanced network selection (ENS) as required by AT&T doc 13340.
// The ENS information is stored in SIM file EF_CSP. If ENS controls, if the manual network
// selection menu should be available in the MMI.
// With Windows Mobile 6.x this menu can be disabled with the registry key Security\Phone\ManualDisable.
// The RIL will set this registry key according to the content of the SIM file.
// Any change of the content of the SIM file requires a change to automatic network selection mode.
// The RIL registry key "CheckNSMCSetting" activates this feature.
//
// Parameter: bInitial: if set, this is the first call of the function after the module has been started.
//
void CRilHandle::CheckENSSetting(BOOL bInitial)
{
    ENS_SETTING nENS = ENS_UNKNOWN;
    char szCmd[MAX_ATCMD_LEN];
    char szPath[MAX_PATH];

    if (bInitial) {
        DWORD dwTmp = 0;

        m_nCurrentENSSetting = ENS_UNKNOWN;
        (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("CheckNSMCSetting"), &dwTmp);
        if (dwTmp != 1 || IsForbiddenSIMFile(SIM_EF_CSP))
            return;
    }
    else {
        // If feature is disabled or the EF_CSP SIM file is not accessible, do not execute this command.
        if (m_nCurrentENSSetting == ENS_UNKNOWN)
            return;
    }
    // Read file EF_CSP and check the bit PLMN mode bit.
    if (NeedSIMPath(GetCurrentSIMType(), SIM_EF_CSP, szPath, MAX_PATH))
        (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CRSM=176,%d,0,0,26,,\"%s\"\r", SIM_EF_CSP, szPath);
    else
        (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CRSM=176,%d,0,0,26\r", SIM_EF_CSP);
    (void)QueueInternalCmdBlocking(HCCMD_STANDARD, this, szCmd, CMDOPT_NONE, APIID_SENDRESTRICTEDSIMCMD, 0, 0, 0, ParseCheckENSSetting, NULL, (LPVOID)&nENS);

    if (m_nCurrentENSSetting != nENS || bInitial) {
        DWORD dwBlob = (DWORD)nENS;
        UINT dwACT = NO_INIT_COPS_SETTING;
        DWORD dwRegKey = 0;

        (void)BroadcastDWORDBlobNotification(RIL_NOTIFY_DEVSPECIFIC_NSMC_STATUS_CHANGE, dwBlob);

        if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, TEXT("Security\\Phone"), TEXT("ManualDisable"), &dwRegKey)) {
            // The registry key "ManualDisable" enables/disables the menu for the manual network selection.
            if (nENS == ENS_MENU_DISABLED)
                SetRegistryDWORD(HKEY_LOCAL_MACHINE, TEXT("Security\\Phone"), TEXT("ManualDisable"), 1);
            if (nENS == ENS_MENU_ENABLED || (nENS == ENS_UNKNOWN && bInitial))
                SetRegistryDWORD(HKEY_LOCAL_MACHINE, TEXT("Security\\Phone"), TEXT("ManualDisable"), 0);
        }

        // AT&T requires to perform an automatic network selection now.
        if (bInitial) {
            // If this function is called during initialization, set the initialization operator setting to automatic.
            g_iSetCOPSinInit = 0;
        }
        else {
            // To use the correct AcT setting, the RIL first queries the current setting.
            // If the AT+COPS/<AcT> workaround is active there is no need to query
            // the current AcT value sunbce the right AcT is already configured with
            // AT^SCFG=Radio/Band.
            if (!AVOID_COPS_ACT)
                (void)QueueInternalCmdBlocking(HCCMD_STANDARD, this, "AT+COPS?\r", CMDOPT_NONE, APIID_GETCURRENTOPERATOR, 0, 0, 0, ParseGetCurrentOperator, NULL, (LPVOID)&dwACT);
            // The read command of AT+COPS may use other values than the write command.
            if (dwACT == 3) dwACT = 0;
            if (dwACT == 4 || dwACT == 6) dwACT = 2;
            if (dwACT == NO_INIT_COPS_SETTING)
                (void)strncpyz(szCmd,"AT+COPS=0\r", MAX_ATCMD_LEN);
            else
                (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+COPS=0,2,,%d", dwACT);
            g_iLastCOPS = 0;
            (void)QueueInternalCmd(HCCMD_STANDARD, this, szCmd, CMDOPT_NONE, APIID_REGISTERONNETWORK, 0, 0, 0);
        }
    }
    m_nCurrentENSSetting = nENS;
    return;
}


//
// Returns the current technology (UMTS or CDMA) based on
// current module type and multi-mode configuration.
TECHNOLOGY_MODE CRilHandle::GetCurrentTechnology(void) const
{
    TECHNOLOGY_MODE nMode = TEC_MODE_UNKNOWN;

    if (GLOBAL_MODE_RADIO) {
        // Use current value of multi-mode setting to determine
        // the current technology in use.
        switch (m_nMultiModeSetting) {
            case MULTIMODE_CONFIG_LTE:
                nMode = TEC_MODE_LTE;
                break;
            case MULTIMODE_CONFIG_CDMA:
                nMode = TEC_MODE_CDMA;
                break;
            default:
                nMode = m_nCurrentTechnology;
        }
    }
    else if (CDMA_ONLY_RADIO) {
        // CDMA only radio.
        nMode = TEC_MODE_CDMA;
    }
    else {
        // All the other modules only support UMTS.
        nMode = TEC_MODE_LTE;
    }

    return nMode;
}

#ifdef RILDRV_CE6
// Visual Studio sees a missing parameter for the CreateProcess() function,
// which is only mandatory in Windows XP.
#pragma warning (push)
#pragma warning ( disable : 6309 6387 6054 )
#endif
//
// Needed for PXS8 in multi-mode: if the PXS8 switches between CDMA and GSM/UMTS
// The operating system needs to be notified about the change. These operations are
// needed on a technology change:
// 1) Send RIL_NOTIFY_SYSTEMCHANGED notification
// 2) Update the "Features" registry keys in HKCU\ControlPanel\Phone and HKLM\Security\Phone
// 3) Call external application (optional). This can be used to update the data configuration with an XML file
//
// Parameter: nMode: new technology (CDMA or UMTS)
void CRilHandle::NotifyNewTechnologyMode(TECHNOLOGY_MODE nMode)
{
    SYNCBLOCK(m_csTechnologySwitch);
    DWORD dwControlPanelFeaturesCdma;
    DWORD dwControlPanelFeaturesUmts;
    DWORD dwControlPanelFeaturesOld;
    DWORD dwSecuritylFeaturesCdma;
    DWORD dwSecurityFeaturesUmts;
    DWORD dwSecurityFeaturesOld;
    WCHAR wszExecutablePath[MAX_PATH];

    m_nCurrentTechnology = nMode;
    memset(wszExecutablePath, 0, sizeof(wszExecutablePath));
    // Read configuration valuers from the registry. The default vaules are the
    // values recommended in the Microsoft Platform Documentation.
    if (!GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("ControlPanelFeaturesCdma"), &dwControlPanelFeaturesCdma))
        dwControlPanelFeaturesCdma = 0x0002B820;
    if (!GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("ControlPanelFeaturesUmts"), &dwControlPanelFeaturesUmts))
        dwControlPanelFeaturesUmts = 0x0206C7D8;
    if (!GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("SecurityFeaturesCdma"), &dwSecuritylFeaturesCdma))
        dwSecuritylFeaturesCdma = 0x00;
    if (!GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("SecurityFeaturesUmts"), &dwSecurityFeaturesUmts))
        dwSecurityFeaturesUmts = 0x18;
    // Save old values, to compare them with the new ones.
    (void)GetRegistryDWORD(HKEY_CURRENT_USER, L"ControlPanel\\Phone", L"Features", &dwControlPanelFeaturesOld);
    (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, L"Security\\Phone", L"Features", &dwSecurityFeaturesOld);

    if (nMode == TEC_MODE_CDMA) {
        if (dwControlPanelFeaturesCdma != dwControlPanelFeaturesOld || dwSecuritylFeaturesCdma != dwSecurityFeaturesOld) {
            RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: Apply CDMA settings\r\n")));
            (void)SetRegistryDWORD(HKEY_CURRENT_USER, L"ControlPanel\\Phone", L"Features", dwControlPanelFeaturesCdma);
            (void)SetRegistryDWORD(HKEY_LOCAL_MACHINE, L"Security\\Phone", L"Features", dwSecuritylFeaturesCdma);
            (void)BroadcastDWORDBlobNotification(RIL_NOTIFY_SYSTEMCHANGED, RIL_SYSTEMTYPE_1XRTTPACKET|RIL_SYSTEMTYPE_IS95A|RIL_SYSTEMTYPE_IS95B);
            (void)BroadcastDWORDBlobNotification(RIL_NOTIFY_SYSTEMCAPSCHANGED, RIL_SYSTEMCAPS_VOICEDATA);
            if (!GetRegistrySZ(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("ConfigExeCdma"), wszExecutablePath, MAX_PATH))
                memset(wszExecutablePath, 0, sizeof(wszExecutablePath));
            RHA_SwitchToCDMA();
        }
        if (!m_fCdmaUrcsActivated) {
            DWORD dwReg = 1;

            ReadPostInitRegKeys();

            if (!GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("UseCdmaCtzuSetting"), &dwReg))
                dwReg = 1;
            m_fCdmaUrcsActivated = TRUE;
            // The following CDMA specific URC may be not active yet.
            if (AVOID_SIND_RSSI_IN_CDMA)
                (void)QueueInternalCmd(HCCMD_URCPORT_ONLY, this, "AT^SREG=1;^SIND=audio,1\r", CMDOPT_URC, APIID_ENABLEDISABLEURCS, 0, 0, 0);
            else if (NO_SIND_SIGNAL_IN_CDMA)
                (void)QueueInternalCmd(HCCMD_URCPORT_ONLY, this, "AT^SREG=1;^SIND=rssi,1;^SIND=audio,1\r", CMDOPT_URC, APIID_ENABLEDISABLEURCS, 0, 0, 0);
            else
                (void)QueueInternalCmd(HCCMD_URCPORT_ONLY, this, "AT^SREG=1;^SIND=rssi,1;^SIND=signal,1;^SIND=audio,1;+CRC=1\r", CMDOPT_URC, APIID_ENABLEDISABLEURCS, 0, 0, 0);
            if (dwReg == 1) {
                // Additionally it might be necessary to activate CTZU.
                (void)QueueInternalCmd(HCCMD_URCPORT_ONLY, this, "AT+CTZU=1\r", CMDOPT_URC, APIID_ENABLEDISABLEURCS, 0, 0, 0);
            }
            if (!GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("UseCdmaCCWA"), &dwReg))
                dwReg = 1;
            if (dwReg == 1) {
                // If configured, activate call waiting indication for CDMA.
                (void)QueueInternalCmd(HCCMD_URCPORT_ONLY, this, "AT+CCWA=1\r", CMDOPT_URC, APIID_ENABLEDISABLEURCS, 0, 0, 0);
            }

            // Set time in module to system time of Windows Mobile.
            (void)SetTimeInModule();

            // Initialize NITZ (if configured)
            InitializeNITZ();
        }
    }
    else if (nMode == TEC_MODE_LTE) {
        if (dwControlPanelFeaturesUmts != dwControlPanelFeaturesOld || dwSecurityFeaturesUmts != dwSecurityFeaturesOld) {
            RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: Apply GSM/UMTS settings\r\n")));
            (void)SetRegistryDWORD(HKEY_CURRENT_USER, L"ControlPanel\\Phone", L"Features", dwControlPanelFeaturesUmts);
            (void)SetRegistryDWORD(HKEY_LOCAL_MACHINE, L"Security\\Phone", L"Features", dwSecurityFeaturesUmts);
            // Module might have been switched to IRA because of previous CDMA registration.
            (void)QueueInternalCmd(HCCMD_URCPORT_ONLY, this, "AT+CSCS=\"UCS2\"\r", CMDOPT_URC, APIID_ENABLEDISABLEURCS, 0, 0, 0);
#ifdef SUPPORTS_EHSX_R1
            if (NO_SIND_INDICATIONS)
                (void)QueueInternalCmd(HCCMD_STANDARD, this, "AT+XREG?\r", CMDOPT_URC, APIID_GETCURRENTSYSTEMTYPE, 0, 0, 0, ParseInitialXREGInfo, NULL, (LPVOID)this);
            else
#endif
                (void)QueueInternalCmd(HCCMD_STANDARD, this, "AT^SIND=psinfo,2\r", CMDOPT_URC, APIID_GETCURRENTSYSTEMTYPE, 0, 0, 0, ParseInitialPSInfo, ParseInitialPSInfoError, (LPVOID)this);
            if (NO_SIND_SIGNAL_IN_CDMA)
                (void)QueueInternalCmd(HCCMD_STANDARD, this, "AT^SIND=signal,1\r", CMDOPT_URC, APIID_GETCURRENTSYSTEMTYPE, 0, 0, 0);
            if (NO_SIND_NITZ_IN_CDMA)
                (void)QueueInternalCmd(HCCMD_STANDARD, this, "AT^SIND=nitz,1\r", CMDOPT_URC, APIID_GETCURRENTSYSTEMTYPE, 0, 0, 0);
            if (!GetRegistrySZ(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("ConfigExeUmts"), wszExecutablePath, MAX_PATH))
                memset(wszExecutablePath, 0, sizeof(wszExecutablePath));
            RHA_SwitchToUMTS();
        }
        if (!m_fGsmUrcsActivated) {
            m_fGsmUrcsActivated = TRUE;
            // The following GSM specific URC may be not active yet.
            if(!SUPPORTS_ONLY_DATAMODE){
            	(void)QueueInternalCmd(HCCMD_URCPORT_ONLY, this, "AT+CR=1;+CGREG=1;+COLP=1;+CCWA=1;+CLIP=1;+CUSD=1;+CRC=1\r", CMDOPT_URC, APIID_ENABLEDISABLEURCS, 0, 0, 0);
            }else{
				(void)QueueInternalCmd(HCCMD_URCPORT_ONLY, this, "AT+CR=1;+CGREG=1;+CUSD=1\r", CMDOPT_URC, APIID_ENABLEDISABLEURCS, 0, 0, 0);				
			}
		}
    }

    // If given, execute external application which can do further configurations
    // like installing XML files.
    if (wcslen(wszExecutablePath) > 0) {
        if (!CreateProcess(wszExecutablePath, NULL, NULL, NULL, FALSE, 0, NULL, NULL, NULL, NULL)) {
            RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: Cannot create process %s\r\n"), wszExecutablePath));
        }
    }
}
#ifdef RILDRV_CE6
#pragma warning (pop)
#endif

//
// Searches for a CDMA PRL file. If found, this function returns file
// name and file version.
// The file name has to have the following format: cdma_xxxxx.prl. The
// "xxxxx" stands for the PRL list version (1-5 digits).
//
// Parameters:
// wszFileName [out]: if file found, name of the PRL file (full path name)
// nLen [in]: size of wszFileName
// pdwVersionNr [out]: if file found, version of the file (-1 if no file found)
//
// Return value: TRUE, if an PRL file has been found
//
BOOL CRilHandle::GetCdmaPrlFilename(WCHAR *wszFileName, UINT nLen, DWORD *pdwVersionNr, char *szProvider) const
{
    BOOL fRet = FALSE;
    WCHAR wszSearchPath[MAX_PATH];
    WCHAR wszSearchPatttern[MAX_PATH];
    WCHAR wszTempFileName[MAX_PATH];

    // Print: check if there is an internal list present. This is available
    // for the providers Sprint and Verizon.
    if (strcmp(szProvider, "Sprint") == 0 && s_uiSizeSprintPRL > 0) {
        DWORD dwReg = 0;

        (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("DisableInternalPrlSprint"), &dwReg);
        if (dwReg == 0) {
            if (wszFileName != NULL)
                (void)wcsncpy(wszFileName, INTERNAL_PRL_SPRINT, nLen);
            *pdwVersionNr = s_uiVersionSprintPRL;
            return TRUE;
        }
    }
    if (strcmp(szProvider, "Verizon") == 0 && s_uiSizeVerizonPRL > 0) {
        DWORD dwReg = 0;

        (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("DisableInternalPrlVerizon"), &dwReg);
        if (dwReg == 0) {
            if (wszFileName != NULL)
                (void)wcsncpy(wszFileName, INTERNAL_PRL_VERIZON, nLen);
            *pdwVersionNr = s_uiVersionVerizonPRL;
            return TRUE;
        }
    }

    memset(wszSearchPath, 0, sizeof(wszSearchPath));
    memset(wszTempFileName, 0, sizeof(wszSearchPath));
    if (!GetRegistrySZ(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("DefaultPrlSearchPath"), wszSearchPath, MAX_PATH)) {
        wcscpy(wszSearchPath, L"\\Storage Card");
    }
    *pdwVersionNr = 0xFFFFFFFF;

    if (strlen(szProvider) == 0)
        (void)wcsncpyz(wszSearchPatttern, L"cdma*.prl", MAX_PATH);
    else
        (void)_snwprintfz(wszSearchPatttern, MAX_PATH, L"cdma_%S*.prl", szProvider);

    fRet = GetConfigFilename(wszTempFileName, MAX_PATH, wszSearchPatttern, wszSearchPath);
    if (wszFileName != NULL)
        (void)wcsncpy(wszFileName, wszTempFileName, nLen);

    if (fRet)
    {
        WCHAR *pwszT;

        pwszT = wcsstr(wszTempFileName, L".prl");
        if (pwszT == NULL)
            pwszT = wcsstr(wszTempFileName, L".PRL");
        if (pwszT != NULL)
        {
            *pwszT = '\0';
            pwszT = wcsrchr(wszTempFileName, '_');
            if (pwszT != NULL && pwszT != wszTempFileName)
                *pdwVersionNr = (DWORD)_ttoi((pwszT+1));
        }

    }

    return fRet;
}

BOOL CRilHandle::GetCDMAProviderName(COM_PORT_TYPE PortType, char *szProvider)
{
    char szOriginal[MAX_ATCMD_LEN];
    BOOL fRet = FALSE;

    memset(szOriginal, 0, MAX_ATCMD_LEN);
    memset(m_szCdmaProvider, 0, MAX_CDMA_PROVIDER_NAME_LEN);
    szProvider[0] = '\0';
    if (!MULTIPLE_CDMA_PRL_LIST) return TRUE;

    // Read current CDMA operator
    fRet = CheckSCFGSetting(PortType, "CDMA/Operator/Store", NULL, szOriginal, NULL, 0);
    if (fRet) {
        if (strlen(szOriginal) > MAX_CDMA_PROVIDER_NAME_LEN) {
            fRet = FALSE;
        }
        else {
            // Store provider name, might be needed later.
            (void)strncpyz(m_szCdmaProvider, szOriginal, MAX_CDMA_PROVIDER_NAME_LEN);
            // "None": module not configured to any CDMA provider.
            if (_stricmp(szOriginal, "None") != 0)
                (void)strncpyz(szProvider, szOriginal, MAX_CDMA_PROVIDER_NAME_LEN);
        }
    }

    return fRet;
}

//
// Check current version of the PRL list.
//
BOOL CRilHandle::CheckCdmaPrlVersion(COM_PORT_TYPE PortType, WCHAR *wszFileName, WCHAR *wszRegKey)
{
    DWORD dwStartPos, dwEndPos;
    DWORD dwProfilePRLVersion;
    DWORD dwModulePRLVersion;
    DWORD dwReg = 0;
    char szResponse[MAX_DEFAULT_INIT_AT_RESPONSE];
    char szProvider[MAX_CDMA_PROVIDER_NAME_LEN];

    if (!GetCDMAProviderName(PortType, szProvider)) return FALSE;

    // The name of the registry key for enable/disable update depends on the
    // selected CDMA provider.
    if (wszRegKey != NULL)
        (void)_snwprintfz(wszRegKey, MAX_PATH, L"CdmaLoadDefaultPrl%S", szProvider);

    dwProfilePRLVersion = 0;
    if (!ExSendATCmdGetRsp(PortType, "AT$PRL?\r", szResponse, sizeof(szResponse), GetPreMuxInitTimeout(PREMUX_TIMEOUTS_SBNR), 0))
    {
        goto Error;
    }
    dwStartPos = 0;
    if(!FindString(dwStartPos, szResponse, "$PRL: ", 64, &dwEndPos))
    {
        return FALSE;
    }
    dwModulePRLVersion = (DWORD)atoi((char*)&szResponse[dwEndPos]);

    // If no space for the file name is given, this call was just about checking
    // the current PRL version.
    if (wszFileName == NULL || wszRegKey == NULL) return FALSE;

    // If load of default PRL is disabled, no further checks are necessary.
    (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, wszRegKey, &dwReg);
    if (dwReg == 0)
    {
        return FALSE;
    }

    // If there is no PRL file, there is no need to make further checks.
    if (!GetCdmaPrlFilename(wszFileName, MAX_PATH, &dwProfilePRLVersion, szProvider))  {
        return FALSE;
    }

    // Compare version numbers to determine if an update is necessary.
    if(dwProfilePRLVersion != dwModulePRLVersion)
    {
        WCHAR wszForceUpdateKey[MAX_PATH];

        (void)_snwprintfz(wszForceUpdateKey, MAX_PATH, L"CdmaPrlForceUpdate%S", szProvider);
        dwReg = 0;
        if (!GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, wszForceUpdateKey, &dwReg)) {
            // If there is no provider spezific key, there might be a global one.
            (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, L"CdmaPrlForceUpdate", &dwReg);
        }
        if (dwModulePRLVersion < dwProfilePRLVersion || dwReg == 1) {
            RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: CDMA PRL list needs an update\r\n")));
            return TRUE;
        }
    }

Error:

    return FALSE;
}

//
// Performs an update of the PRL list using a given PRL list file (.PRL).
//
BOOL CRilHandle::DownloadCdmaPrlList(COM_PORT_TYPE PortType, WCHAR *wszFileName, WCHAR *wszRegKey)
{
    CNotificationData* pnd = NULL;
    BOOL fSendEndNotification = FALSE;
    BOOL fRet = FALSE;
    HANDLE hFile = INVALID_HANDLE_VALUE;
    BYTE *szBytes = NULL;
    DWORD nFileSize;
    DWORD dwByteSize;
    char  szResponse[MAX_ENHANCED_INIT_AT_RESPONSE];
    DWORD dwPrlTimeout = CDMA_PRL_UPDATE_TIMEOUT;
    DWORD dwPrlDelay = CDMA_PRL_UPDATE_DELAY;
    DWORD dwPrlInitialDelay = CDMA_PRL_UPDATE_INIT_DELAY;
    BOOL fNonCriticalError = FALSE;
    char szCmd[MAX_ATCMD_LEN];
    const unsigned char *cpbInternalBuffer = NULL;

    if (wcscmp(wszFileName, INTERNAL_PRL_SPRINT) == 0) {
        nFileSize = s_uiSizeSprintPRL;
        cpbInternalBuffer = s_bSprintPRL;
    }
    else if (wcscmp(wszFileName, INTERNAL_PRL_VERIZON) == 0) {
        nFileSize = s_uiSizeVerizonPRL;
        cpbInternalBuffer = s_bVerizonPRL;
    }
    else {
        hFile = CreateFile(wszFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
        if (hFile == INVALID_HANDLE_VALUE)
        {
            RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: Cannot open PRL file \"%s\"\r\n"), wszFileName));
            goto Error;
        }
        nFileSize = GetFileSize(hFile, NULL);
    }

    (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("CdmaPrlUpdateTimeout"), &dwPrlTimeout);
    (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("CdmaPrlUpdateDelay"), &dwPrlDelay);
    (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("CdmaPrlUpdateInitDelay"), &dwPrlInitialDelay);

    RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: PRL Update File: %s\r\n"), wszFileName));

    pnd = new CNotificationData;
    if (pnd)
    {
        // Send string including terminating '\0'.
        if (pnd->InitFromRealBlob(RIL_NOTIFY_DEVSPECIFIC_CDMA_PRL_UPDATE_START, (void*)wszFileName, (2*wcslen(wszFileName))+sizeof(WCHAR)))
            BroadcastNotification(pnd);
        else
            delete pnd;
        fSendEndNotification = TRUE;
    }

    Sleep(dwPrlInitialDelay);

    szBytes = (BYTE *)AllocBlob(nFileSize);
    if (hFile == INVALID_HANDLE_VALUE) {
        // Copy from internal list.
        memcpy(szBytes, cpbInternalBuffer, nFileSize);
    }
    else {
        // Read from file
        if(!ReadFile(hFile,szBytes, nFileSize, &dwByteSize, NULL) && dwByteSize != nFileSize)
        {
            RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: Can't read CDMA PRL list file\r\n")));
            goto Error;
        }
    }
    if (FILELEN_FOR_SBNW_PRL)
        (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT^SBNW=\"PRL\",%d\r", nFileSize);
    else
        strncpyz(szCmd, "AT^SBNW=\"PRL\",2\r", MAX_ATCMD_LEN);
    if (!ExSendATCommand(PortType, szCmd))
        goto Error;
    if (!ExGetATResponse(PortType, szResponse, sizeof(szResponse), dwPrlTimeout, FALSE, TRUE))
        goto Error;
    // send the data to the module
    if (!ExSendBytes(PortType, szBytes, nFileSize, dwPrlDelay))
        goto Error;
    if (!ExGetATResponse(PortType, szResponse, sizeof(szResponse), dwPrlTimeout, TRUE, TRUE))
        goto Error;
    if (strstr(szResponse, "ERROR") != NULL)  { // There are various possible error messages, but all contain the word "ERROR"
        RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: PRL list update FAIL\r\n")));
        fNonCriticalError = TRUE;
        goto Error;
    }
    // There is only one (small) block, so the progress indicator is just for
    // compatibility with the other progress indications and goes directly to
    // 100%.
    BroadcastDWORDBlobNotification(RIL_NOTIFY_DEVSPECIFIC_CDMA_PRL_UPDATE_PROGRESS, 100);
    (void)PurgeComm(g_ComManager.GetPortHandle(PortType), PURGE_RXCLEAR | PURGE_TXCLEAR);

    // To verify, that the new PRL list is really loaded, query again the PRL list version.
    (void)CheckCdmaPrlVersion(PortType, NULL, NULL);
    fRet = TRUE;

    // To avoid further PRL updates which might overwrite PRL lists sent by the
    // network the RIL resets the registry key.
    (void)SetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, wszRegKey, 0);
Error:

    if (fSendEndNotification)
        BroadcastDWORDBlobNotification(RIL_NOTIFY_DEVSPECIFIC_CDMA_PRL_UPDATE_END,  fRet ? UPDATE_PROCESS_SUCCESS : UPDATE_PROCESS_ERROR);

    if (hFile != INVALID_HANDLE_VALUE)
        CloseHandle(hFile);

    // In case of a non-critical error, allow RIL to go on, so let the function return "true".
    if (fNonCriticalError) fRet = TRUE;

    return fRet;
}

//
// Searches for a CDMA PRI file. If found, this function returns file
// name and file version.
// The file name has to have the following format: cdma_xxxxx.pri. The
// "xxxxx" stands for the PRI configuration version (1-5 digits).
//
// Parameters:
// wszFileName [out]: if file found, name of the PRI file (full path name)
// nLen [in]: size of wszFileName
// pdwVersionNr [out]: if file found, version of the file (-1 if no file found)
//
// Return value: TRUE, if an PRI file has been found
//
BOOL CRilHandle::GetCdmaPriFilename(WCHAR *wszFileName, UINT nLen, DWORD *pdwVersionNr) const
{
    BOOL fRet = FALSE;
    WCHAR wszSearchPath[MAX_PATH];
    WCHAR wszTempFileName[MAX_PATH];

    memset(wszSearchPath, 0, sizeof(wszSearchPath));
    memset(wszTempFileName, 0, sizeof(wszSearchPath));
    if (!GetRegistrySZ(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("DefaultPriSearchPath"), wszSearchPath, MAX_PATH)) {
        wcscpy(wszSearchPath, L"\\Storage Card");
    }
    *pdwVersionNr = 0xFFFFFFFF;

    fRet = GetConfigFilename(wszTempFileName, MAX_PATH, L"cdma*.pri", wszSearchPath);
    if (wszFileName != NULL)
        (void)wcsncpy(wszFileName, wszTempFileName, nLen);
    if (fRet)
    {
        WCHAR *pwszT;

        pwszT = wcsstr(wszTempFileName, L".pri");
        if (pwszT == NULL)
            pwszT = wcsstr(wszTempFileName, L".PRI");
        if (pwszT != NULL)
        {
            *pwszT = '\0';
            pwszT = wcsrchr(wszTempFileName, '_');
            if (pwszT != NULL && pwszT != wszTempFileName)
                *pdwVersionNr = (DWORD)_ttoi((pwszT+1));
        }

    }
    return fRet;
}

//
// Check current version of the PRI configuration.
//
BOOL CRilHandle::CheckCdmaPriVersion(COM_PORT_TYPE /*PortType*/)
{
    DWORD dwProfilePRIVersion;
    DWORD dwModulePRIVersion = 0;

    (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("CurrentCdmaPriVersion"), &dwModulePRIVersion);

    dwProfilePRIVersion = 0;

    // If there is no PRI file, there is no need to make further checks.
    if (!GetCdmaPriFilename(NULL, 0, &dwProfilePRIVersion))  {
        return FALSE;
    }

    // Compare version numbers to determine if an update is necessary.
    if(dwProfilePRIVersion != dwModulePRIVersion)
    {
        RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: CDMA PRI configuration needs an update\r\n")));
        return TRUE;
    }

    return FALSE;
}

//
// Performs an update of the PRI configuration using a given PRI file (.PRI).
//
BOOL CRilHandle::DownloadCdmaPriConfig(COM_PORT_TYPE PortType)
{
    CNotificationData* pnd = NULL;
    BOOL fSendEndNotification = FALSE;
    BOOL fRet = FALSE;
    HANDLE hFile = INVALID_HANDLE_VALUE;
    BYTE *szBytes = NULL;
    DWORD dwProfilePRIVersion;
    DWORD nFileSize;
    DWORD dwByteSize;
    WCHAR wszFileName[MAX_PATH];
    char  szResponse[MAX_BINARY_INIT_AT_RESPONSE];
    DWORD dwPriTimeout = CDMA_PRI_UPDATE_TIMEOUT;
    DWORD dwPriDelay = CDMA_PRI_UPDATE_DELAY;
    DWORD dwPriInitialDelay = CDMA_PRI_UPDATE_INIT_DELAY;
    BOOL fNonCriticalError = FALSE;
    WCHAR wszSPC[MAX_ATCMD_LEN];
    char szCmd[MAX_ATCMD_LEN];
    DWORD dwFileFormat = 2;

    if (!GetCdmaPriFilename(wszFileName, MAX_PATH, &dwProfilePRIVersion))
    {
        fRet = TRUE;
        goto Error;
    }
    hFile = CreateFile(wszFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile == INVALID_HANDLE_VALUE)
    {
        RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: Cannot open PRI file \"%s\"\r\n"), wszFileName));
        goto Error;
    }
    nFileSize = GetFileSize(hFile, NULL);

    (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("CdmaPriUpdateTimeout"), &dwPriTimeout);
    (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("CdmaPriUpdateDelay"), &dwPriDelay);
    (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("CdmaPriUpdateInitDelay"), &dwPriInitialDelay);

    (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("CdmaPriUpdateFileFormat"), &dwFileFormat);

    RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: PRI Update File Version: %d\r\n"), dwProfilePRIVersion));

    // For PXS8 Rel2 (at least customer samples) it is required to program the SPC first.
    memset(wszSPC, 0, sizeof(wszSPC));
    if (GetRegistrySZ(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("SetPRI_SPC"), wszSPC, MAX_ATCMD_LEN)) {
        if (!INCLUDE_SPC_FOR_SBNW_PRI) {
            (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT^SCFG=Security/SPC,\"%S\"\r", wszSPC);
            if (!ExSendATCmdGetRsp(PortType, szCmd, szResponse, sizeof(szResponse), GetPreMuxInitTimeout(PREMUX_TIMEOUTS_DEFAULT), 0))
                goto Error;
        }
    }
    else {
        wcscpy(wszSPC, L"000000");
    }

    pnd = new CNotificationData;
    if (pnd)
    {
        // Send string including terminating '\0'.
        if (pnd->InitFromRealBlob(RIL_NOTIFY_DEVSPECIFIC_CDMA_PRI_UPDATE_START, (void*)wszFileName, (2*wcslen(wszFileName))+sizeof(WCHAR)))
            BroadcastNotification(pnd);
        else
            delete pnd;
        fSendEndNotification = TRUE;
    }

    Sleep(dwPriInitialDelay);

    szBytes = (BYTE *)AllocBlob(nFileSize);
    if(!ReadFile(hFile,szBytes, nFileSize, &dwByteSize, NULL) && dwByteSize != nFileSize)
    {
        RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: Can't read CDMA PRI file\r\n")));
        goto Error;
    }
    if (INCLUDE_SPC_FOR_SBNW_PRI)
        (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT^SBNW=\"PRI\",%d,\"%S\"\r", dwFileFormat, wszSPC);
    else
        strncpyz(szCmd, "AT^SBNW=\"PRI\",2\r", MAX_ATCMD_LEN);
    if (!ExSendATCommand(PortType, szCmd))
        goto Error;
    if (!ExGetATResponse(PortType, szResponse, sizeof(szResponse), dwPriTimeout, FALSE, TRUE))
        goto Error;
    // send the data to the module
    if (!ExSendBytes(PortType, szBytes, nFileSize, dwPriDelay))
        goto Error;
    if (!ExGetATResponse(PortType, szResponse, sizeof(szResponse), dwPriTimeout, TRUE, TRUE))
        goto Error;
    if (strstr(szResponse, "ERROR") != NULL)  { // There are various possible error messages, but all contain the word "ERROR"
        RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: PRI update FAIL\r\n")));
        fNonCriticalError = TRUE;
        goto Error;
    }
    // There is only one (small) block, so the progress indicator is just for
    // compatibility with the other progress indications and goes directly to
    // 100%.
    BroadcastDWORDBlobNotification(RIL_NOTIFY_DEVSPECIFIC_CDMA_PRI_UPDATE_PROGRESS, 100);
    (void)PurgeComm(g_ComManager.GetPortHandle(PortType), PURGE_RXCLEAR | PURGE_TXCLEAR);

    fRet = TRUE;
    // Write current version to registry.
    (void)SetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("CurrentCdmaPriVersion"), dwProfilePRIVersion);

Error:

    if (fSendEndNotification)
        BroadcastDWORDBlobNotification(RIL_NOTIFY_DEVSPECIFIC_CDMA_PRI_UPDATE_END,  fRet ? UPDATE_PROCESS_SUCCESS : UPDATE_PROCESS_ERROR);

    if (hFile != INVALID_HANDLE_VALUE)
        CloseHandle(hFile);

    // In case of a non-critical error, allow RIL to go on, so let the function return "true".
    if (fNonCriticalError) fRet = TRUE;

    return fRet;
}

//
// The module always stores a network history while graceful shutdown. After
// possible non-graceful shutdown (e.g. battery removal), in rare cases it
// was observed that due to inconsistent network history, proper network
// registration failed. To avoid the described situation, the RIL can reset
// the network history (acquisition database) on first startup after warm/cold
// boot of the OS. After such reset, a hard shutdown is required via RHA_RadioPWROff.
// This option can be activated with the following registry key:
// [HKEY_LOCAL_MACHINE\Software\Microsoft\RIL]
//  "ResetACQDB"=dword:1
//
// Parameter:
//      pbNeedRestart [out]: is set to TRUE if a restart of the module is now necessary
//
// Return: TRUE: AcqDB reset unnecessary, no further action necessary.
//         FALSE: shutdown necessary, either because of an error (pbNeedRestart == FALSE),
//                or if restart is needed (pbNeedRestart == TRUE)
//
BOOL CRilHandle::ResetAcqDB(COM_PORT_TYPE PortType, BOOL *pbNeedRestart)
{
    char szResponse[MAX_ATCMD_LEN];
    DWORD dwReg = 0;
    BOOL fRet = TRUE;

    *pbNeedRestart = FALSE;
    (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("ResetACQDB"), &dwReg);

    if (!m_fNoCheckForACQDBneeded && dwReg == 1) {
        fRet = FALSE;
        if (ExSendATCmdGetRsp(PortType, "AT^SBNW=\"acqdb\",0\r", szResponse, sizeof(szResponse), GetPreMuxInitTimeout(), 0))
            *pbNeedRestart = TRUE;
    }
    m_fNoCheckForACQDBneeded = TRUE;
    return fRet;
}

//
// Searches for NV Item files. If found any, this function performs an update
// of all NV item files found.
// The file name has to have the following format: nv_xxxxx.bin. The
// "xxxxx" stands for the item number (1-5 digits).
// The file must use the binary format.
//
// Parameters:
// PortType [in]: port on which the update should be performed.
// fNeedReset [out]: will be set to TRUE, if a restart is required
//
// Return value: TRUE, if check for update worked,
//
BOOL CRilHandle::DownloadNvItems(COM_PORT_TYPE PortType, BOOL *fNeedReset)
{
    WCHAR wszSearchPath[MAX_PATH];
    WCHAR wszSearchPattern[MAX_PATH];
    WCHAR wszFileName[MAX_PATH];
    WCHAR wszLastUpdatedNVItems[MAX_ATCMD_LEN];
    HANDLE hFile;
    WIN32_FIND_DATA FindFileData;

    memset(wszSearchPath, 0, sizeof(wszSearchPath));
    memset(wszSearchPattern, 0, sizeof(wszSearchPattern));
    memset(wszLastUpdatedNVItems, 0, sizeof(wszLastUpdatedNVItems));
    if (!GetRegistrySZ(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("DefaultNvSearchPath"), wszSearchPath, MAX_PATH)) {
        wcscpy(wszSearchPath, L"\\Storage Card");
    }
    (void)_snwprintfz(wszSearchPattern, MAX_PATH, L"%s\\nv_*.bin", wszSearchPath);

    (void)GetRegistrySZ(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("LastUpdatedNvItems"), wszLastUpdatedNVItems, MAX_ATCMD_LEN);

    hFile = FindFirstFile(wszSearchPattern, &FindFileData);
    if (hFile  != INVALID_HANDLE_VALUE) {
        BOOL bFound = TRUE;

        while (bFound) {
            if (!IsInListWCHAR(wszLastUpdatedNVItems, FindFileData.cFileName, L",")) {
                RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: Update NV Item %s\r\n"), FindFileData.cFileName));
                (void)_snwprintfz(wszFileName, MAX_PATH, L"%s\\%s", wszSearchPath, FindFileData.cFileName);
                if (DownloadNvItem(PortType, wszFileName)) {
                    DWORD dwRestart = 1;
                    WCHAR wszRegKey[MAX_ATCMD_LEN];

                    memset(wszRegKey, 0, sizeof(wszRegKey));
                    (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("RestartAfterNvUpdate"), &dwRestart);
                    if (dwRestart == 1)
                        *fNeedReset = TRUE;
                    (void)GetRegistrySZ(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("LastUpdatedNvItems"), wszRegKey, MAX_ATCMD_LEN);
                    if (wcslen(wszRegKey) > 0)
                        (void)wcsncat(wszRegKey, L",", MAX_ATCMD_LEN - wcslen(wszRegKey));
                    (void)wcsncat(wszRegKey, FindFileData.cFileName, MAX_ATCMD_LEN - wcslen(wszRegKey));
                    (void)SetRegistrySZ(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("LastUpdatedNvItems"), wszRegKey);
                }
            }
            bFound = FindNextFile(hFile, &FindFileData);
        }
        FindClose(hFile);
    }

    return TRUE;
}

//
// Performs an update of an NV item with given BIN file (binary mode only).
//
BOOL CRilHandle::DownloadNvItem(COM_PORT_TYPE PortType, WCHAR *wszFileName)
{
    CNotificationData* pnd = NULL;
    BOOL fSendEndNotification = FALSE;
    BOOL fRet = FALSE;
    HANDLE hFile = INVALID_HANDLE_VALUE;
    BYTE *szBytes = NULL;
    DWORD nFileSize;
    DWORD dwByteSize;
    char  szResponse[MAX_BINARY_INIT_AT_RESPONSE];
    DWORD dwNvTimeout = NV_UPDATE_TIMEOUT;
    DWORD dwNvDelay = NV_UPDATE_DELAY;
    DWORD dwNvInitialDelay = NV_UPDATE_INIT_DELAY;
    BOOL fNonCriticalError = FALSE;

    hFile = CreateFile(wszFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile == INVALID_HANDLE_VALUE)
    {
        RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: Cannot open file %s\r\n"), wszFileName));
        fRet = TRUE;
        goto Error;
    }
    nFileSize = GetFileSize(hFile, NULL);
    (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("NvItemUpdateTimeout"), &dwNvTimeout);
    (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("NvItemiUpdateDelay"), &dwNvDelay);
    (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("NvItemUpdateInitDelay"), &dwNvInitialDelay);

    pnd = new CNotificationData;
    if (pnd)
    {
        // Send string including terminating '\0'.
        if (pnd->InitFromRealBlob(RIL_NOTIFY_DEVSPECIFIC_NVITEM_UPDATE_START, (void*)wszFileName, (2*wcslen(wszFileName))+sizeof(WCHAR)))
            BroadcastNotification(pnd);
        else
            delete pnd;
        fSendEndNotification = TRUE;
    }

    Sleep(dwNvInitialDelay);

    szBytes = (BYTE *)AllocBlob(nFileSize);
    if(!ReadFile(hFile,szBytes, nFileSize, &dwByteSize, NULL) && dwByteSize != nFileSize)
    {
        RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: Can't read NV Item file\r\n")));
        goto Error;
    }
    if (!ExSendATCommand(PortType, "AT^SBNW=\"nv\",0,1\r"))
        goto Error;
    if (!ExGetATResponse(PortType, szResponse, sizeof(szResponse), dwNvTimeout, FALSE, TRUE))
        goto Error;
    // send the data to the module
    if (!ExSendBytes(PortType, szBytes, nFileSize, dwNvDelay))
        goto Error;
    if (!ExGetATResponse(PortType, szResponse, sizeof(szResponse), dwNvTimeout, TRUE, TRUE))
        goto Error;
    if (strstr(szResponse, "ERROR") != NULL)  { // There are various possible error messages, but all contain the word "ERROR"
        RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: NV item update FAIL\r\n")));
        fNonCriticalError = TRUE;
        goto Error;
    }
    // There is only one (small) block, so the progress indicator is just for
    // compatibility with the other progress indications and goes directly to
    // 100%.
    BroadcastDWORDBlobNotification(RIL_NOTIFY_DEVSPECIFIC_NVITEM_UPDATE_PROGRESS, 100);
    (void)PurgeComm(g_ComManager.GetPortHandle(PortType), PURGE_RXCLEAR | PURGE_TXCLEAR);

    fRet = TRUE;

Error:

    if (fSendEndNotification)
        BroadcastDWORDBlobNotification(RIL_NOTIFY_DEVSPECIFIC_NVITEM_UPDATE_END,  fRet ? UPDATE_PROCESS_SUCCESS : UPDATE_PROCESS_ERROR);

    if (hFile != INVALID_HANDLE_VALUE)
        CloseHandle(hFile);

    // In case of a non-critical error, allow RIL to go on, so let the function return "true".
    if (fNonCriticalError) fRet = TRUE;

    return fRet;
}

//
// Performs an update of a file for the module's file system with given file.
//
BOOL CRilHandle::DownloadFsItem(COM_PORT_TYPE PortType, WCHAR *wszFileName, WCHAR *wszModuleFileName)
{
    CNotificationData* pnd = NULL;
    BOOL fSendEndNotification = FALSE;
    BOOL fRet = FALSE;
    HANDLE hFile = INVALID_HANDLE_VALUE;
    BYTE *szBytes = NULL;
    DWORD nFileSize;
    DWORD dwByteSize;
    char  szResponse[MAX_BINARY_INIT_AT_RESPONSE];
    DWORD dwFsTimeout = FS_UPDATE_TIMEOUT;
    DWORD dwFsDelay = FS_UPDATE_DELAY;
    DWORD dwFsInitialDelay = FS_UPDATE_INIT_DELAY;
    BOOL fNonCriticalError = FALSE;
    char szCmd[MAX_ATCMD_LEN];

    hFile = CreateFile(wszFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile == INVALID_HANDLE_VALUE)
    {
        RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: Cannot open file %s\r\n"), wszFileName));
        fRet = TRUE;
        goto Error;
    }
    nFileSize = GetFileSize(hFile, NULL);
    (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("FsItemUpdateTimeout"), &dwFsTimeout);
    (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("FsItemiUpdateDelay"), &dwFsDelay);
    (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("FsItemUpdateInitDelay"), &dwFsInitialDelay);

    pnd = new CNotificationData;
    if (pnd)
    {
        // Send string including terminating '\0'.
        if (pnd->InitFromRealBlob(RIL_NOTIFY_DEVSPECIFIC_FSITEM_UPDATE_START, (void*)wszFileName, (2*wcslen(wszFileName))+sizeof(WCHAR)))
            BroadcastNotification(pnd);
        else
            delete pnd;
        fSendEndNotification = TRUE;
    }

    Sleep(dwFsInitialDelay);

    szBytes = (BYTE *)AllocBlob(nFileSize);
    if(!ReadFile(hFile,szBytes, nFileSize, &dwByteSize, NULL) && dwByteSize != nFileSize)
    {
        RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: Can't read FS Item file\r\n")));
        goto Error;
    }
    (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT^SBNW=\"efs\",\"%S\",%d\r", wszModuleFileName, nFileSize);
    if (!ExSendATCommand(PortType, szCmd))
        goto Error;
    if (!ExGetATResponse(PortType, szResponse, sizeof(szResponse), dwFsTimeout, FALSE, TRUE))
        goto Error;
    // send the data to the module
    if (!ExSendBytes(PortType, szBytes, nFileSize, dwFsDelay))
        goto Error;
    if (!ExGetATResponse(PortType, szResponse, sizeof(szResponse), dwFsTimeout, TRUE, TRUE))
        goto Error;
    if (strstr(szResponse, "ERROR") != NULL)  { // There are various possible error messages, but all contain the word "ERROR"
        RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: FS item update FAIL\r\n")));
        fNonCriticalError = TRUE;
        goto Error;
    }
    // There is only one (small) block, so the progress indicator is just for
    // compatibility with the other progress indications and goes directly to
    // 100%.
    BroadcastDWORDBlobNotification(RIL_NOTIFY_DEVSPECIFIC_FSITEM_UPDATE_PROGRESS, 100);
    (void)PurgeComm(g_ComManager.GetPortHandle(PortType), PURGE_RXCLEAR | PURGE_TXCLEAR);

    fRet = TRUE;

Error:

    if (fSendEndNotification)
        BroadcastDWORDBlobNotification(RIL_NOTIFY_DEVSPECIFIC_FSITEM_UPDATE_END,  fRet ? UPDATE_PROCESS_SUCCESS : UPDATE_PROCESS_ERROR);

    if (hFile != INVALID_HANDLE_VALUE)
        CloseHandle(hFile);

    // In case of a non-critical error, allow RIL to go on, so let the function return "true".
    if (fNonCriticalError) fRet = TRUE;

    return fRet;
}

//
// Searches for file system files. If found any, this function performs an update
// of all files found.
// The name can be in any format, but it is necessary to specifiy the full path
// name in the module via registry key.
//
// Parameters:
// PortType [in]: port on which the update should be performed.
// fNeedReset [out]: will be set to TRUE, if a restart is required
//
// Return value: TRUE, if check for update worked,
//
BOOL CRilHandle::DownloadFsItems(COM_PORT_TYPE PortType, BOOL *fNeedReset)
{
    WCHAR wszSearchPath[MAX_PATH];
    WCHAR wszSearchPattern[MAX_PATH];
    WCHAR wszFileName[MAX_PATH];
    WCHAR wszModuleFileName[MAX_PATH];
    WCHAR wszLastUpdatedFsItems[MAX_ATCMD_LEN];
    HANDLE hFile;
    WIN32_FIND_DATA FindFileData;

    memset(wszSearchPath, 0, sizeof(wszSearchPath));
    memset(wszSearchPattern, 0, sizeof(wszSearchPattern));
    memset(wszLastUpdatedFsItems, 0, sizeof(wszLastUpdatedFsItems));
    (void)GetRegistrySZ(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("DefaultFsSearchPath"), wszSearchPath, MAX_PATH);
    (void)_snwprintfz(wszSearchPattern, MAX_PATH, L"%s\\*", wszSearchPath);

    (void)GetRegistrySZ(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("LastUpdatedFsItems"), wszLastUpdatedFsItems, MAX_ATCMD_LEN);

    hFile = FindFirstFile(wszSearchPattern, &FindFileData);
    if (hFile  != INVALID_HANDLE_VALUE) {
        BOOL bFound = TRUE;

        while (bFound) {
            // Check registry key for full path name in file system.
            memset(wszModuleFileName, 0, sizeof(wszModuleFileName));
            (void)GetRegistrySZ(HKEY_LOCAL_MACHINE, TEXT("Software\\Microsoft\\RIL\\FsFiles"), FindFileData.cFileName, wszModuleFileName, MAX_PATH);
            if (wcslen(wszModuleFileName) > 0 && !IsInListWCHAR(wszLastUpdatedFsItems, FindFileData.cFileName, L",")) {
                RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: Update FS file %s\r\n"), FindFileData.cFileName));
                (void)_snwprintfz(wszFileName, MAX_PATH, L"%s\\%s", wszSearchPath, FindFileData.cFileName);
                if (DownloadFsItem(PortType, wszFileName, wszModuleFileName)) {
                    DWORD dwRestart = 1;
                    WCHAR wszRegKey[MAX_ATCMD_LEN];

                    memset(wszRegKey, 0, sizeof(wszRegKey));
                    (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("RestartAfterFsUpdate"), &dwRestart);
                    if (dwRestart == 1)
                        *fNeedReset = TRUE;
                    (void)GetRegistrySZ(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("LastUpdatedFsItems"), wszRegKey, MAX_ATCMD_LEN);
                    if (wcslen(wszRegKey) > 0)
                        (void)wcsncat(wszRegKey, L",", MAX_ATCMD_LEN - wcslen(wszRegKey));
                    (void)wcsncat(wszRegKey, FindFileData.cFileName, MAX_ATCMD_LEN - wcslen(wszRegKey));
                    (void)SetRegistrySZ(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("LastUpdatedFsItems"), wszRegKey);
                }
            }
            bFound = FindNextFile(hFile, &FindFileData);
        }
        FindClose(hFile);
    }

    return TRUE;
}


