/*****************************************************************************
 * Copyright (C) Gemalto M2M GmbH
 *****************************************************************************
 * Project: IMC-RIL-Driver
 *****************************************************************************
 * Author : Wireless Modules RD SW A2
 *****************************************************************************
 * $Workfile: Y:/z_ms-d/src/working/Quinn/RilDrv/EonsHand.cpp $
 * $Revision: /main/ms-d/36 $
 *     $Date: 2013-03-04 13:13:37 $
 *   $Author: pmackent $
 *****************************************************************************
 * Target:
 *  Microsoft Windows Mobile 5.0
 *****************************************************************************
 * Description:
 *  Gemalto IMC RIL
 *****************************************************************************
 *
 * $Log: Y:/z_ms-d/src/working/Quinn/RilDrv/EonsHand.cpp$ 
 * 
 * /main/ms-d/36   2013-03-04 13:13:37   pmackent 
 * Romove EONS/GSM workaround
 *
 * /main/ms-d/35   2013-02-26 15:07:32   pmackent
 * Adaptions for EHS5 1.290
 *
 * /main/ms-d/34   2013-02-22 15:36:42   pmackent
 * Changed behaviour of AT^SIND=eons in EHS5
 *
 * /main/ms-d/33   2013-02-13 09:38:39   pmackent
 * Solve WCHAR/CHAR missallignment
 *
 * /main/ms-d/32   2012-11-22 11:56:58   pmackent
 * Workaround for blocking AT^SIND=eons on EHS5
 *
 * /main/ms-d/31   2012-11-21 13:30:13   pmackent
 * Basic support of EHS5 Rel2
 *
 * /main/ms-d/30   2012-02-22 13:26:34   ctna0256
 * Encapsulate use of Intel AT command set
 *
 * /main/ms-d/29   2012-01-31 16:38:21   ctna0256
 * Added support for EHS5
 *
 * /main/ms-d/28   2011-09-26 13:34:25   ctna0256
 * Comply with CE6 warning level 4
 *
 * /main/ms-d/27   2011-02-18 14:45:54   ctna0256
 * Read SIM Service Table
 *
 * /main/ms-d/26   2010-11-18 10:46:06   ctna0256
 * Remove Prefast warnings
 *
 * /main/ms-d/25   2010-10-26 10:22:24   ctna0256
 * Use SIM path for all CRSM commands
 *
 * /main/ms-d/24   2010-01-28 15:21:47   ctna0256
 * Handle the case, that the 2G/3G SIM detection is performed while the CCID is blocked by the forbidden SIM file registry key (IPIS100057390)
 * Also handle the case, that the IMSI is in the forbidden SIM file registry key.
 *
 * /main/ms-d/23   2009-09-28 11:54:39   ctna0256
 * Remove Lint warnings
 *
 * /main/ms-d/22   2009-09-04 09:21:33   ctna0256
 * Workaround for wrong 2G/3G-SIM detection in module (IPIS100057390)
 *
 * /main/ms-d/21   2009-06-24 14:27:35   ctna0256
 * Use correct record length for PNN field (IPIS100056061)
 *
 * /main/ms-d/20   2009-04-08 11:37:34   ctna0256
 * Fast registration
 *
 * /main/ms-d/19   2009-04-06 14:20:20   ctna0256
 * Read IMSI before using AT+CRSM
 *
 * /main/ms-d/18   2008-11-21 15:04:34   ctna0450
 * Detect pnn table coding sceme properly
 *
 * /main/ms-d/17   2008-11-21 14:52:16   ctna0450
 * Consider ucs2 coded operator names
 *
 * /main/ms-d/16   2008-11-20 14:55:22   ctna0450
 * Fixed problem with pnn name decoding
 *
 * /main/ms-d/15   2008-11-11 15:56:23   ctna0256
 * Workaround for wrong indicated record length in EONS SIM files
 *
 * /main/ms-d/14   2008-11-10 10:58:33   ctna0256
 * Missing event notification, when EONS is diabled
 *
 * /main/ms-d/13   2008-08-22 10:23:35   ctna0256
 * Set Cinterion copyright text
 *
 * /main/ms-d/12   2008-08-21 09:34:20   ctna0256
 * Prepared for new disclaimer handling
 *
 * /main/ms-d/11   2008-07-22 15:52:50   mp49210
 * Complience to WM7, Lint and PreFast requirements
 *
 * /main/ms-d/10   2008-06-19 16:12:25   mp49210
 * Special handling of US MCC values
 *
 * /main/ms-d/9   2008-06-12 11:38:32   mp49210
 * Workaround for wrong characters in EONS name
 *
 * /main/ms-d/8   2008-04-24 10:42:40   mp49210
 * New options for display service provider name
 *
 * /main/ms-d/7   2008-04-10 15:21:33   mp49210
 * New optional handling of service provider name
 *
 * /main/ms-d/6   2008-04-03 14:41:31   mp49210
 * Introduction of new debug zones
 *
 * /main/ms-d/5   2007-08-31 15:06:41   mp49210
 * Allow the restriction of SIM access for special SIM files.
 *
 * /main/ms-d/4   2007-02-09 15:15:44   mp49210
 * Lint compatibility
 *
 * /main/ms-d/3   2006-12-14 16:41:17   mp49210
 * Modified behaviour of AT+CRSM
 *
 * /main/ms-d/2   2006-11-14 12:28:00   wm36264
 * Use correct ifdefs for info trace
 *
 * /main/ms-d/1   2006-10-25 16:53:24   wm36264
 * First transformation from classic RIL
 *
*/

//////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2008 Gemalto M2M GmbH
// Copyright (C) 2006-2008 Siemens AG
//
// Version: 1.0
// Date:    dummy
//
//////////////////////////////////////////////////////////////////////////////

#include "precomp.h"

DWORD WINAPI InitializeEONSThread(LPVOID pVoid)
{
    CEonsHandling* pHandle = (CEonsHandling *)pVoid;

    return pHandle->EONSInitThreadProc();
}

//////////////////////////////////////////////////////////////////////////////
//
// Public interface
//
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//
// Constructor
//
//////////////////////////////////////////////////////////////////////////////
CEonsHandling::CEonsHandling() {
  DWORD dwTmp;

  InitializeCriticalSection(&m_csRunningCmd);
  InitializeCriticalSection(&m_csRunningReadTable);
  m_SupportType = EONS_DISABLED;
  m_dwLines = 0;
  m_dwPnnEntries = 0;
  m_dwOplEntries = 0;
  m_fIsLongName = FALSE;
  m_wSimSw1 = 0;
  m_wSimSw2 = 0;
  m_wCurrentMCC = 0;
  m_wCurrentMNC = 0;
  m_wCurrentLAC = 0;
  m_pRilDevice = 0;
  m_hReadyEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  m_dwUseServiceProvider = 0;
  m_fEONSTableAvailable = FALSE;
  m_nEONSType = 0;
  m_fDisableEONSSIMReadTable = FALSE;
  m_fMustReadIMSIbeforeReadingSIM = TRUE;
  memset(m_tOplInfo, 0, sizeof(OPL_INFO) * EONS_MAX_OPL);
  memset(m_tPnnInfo, 0, sizeof(PNN_INFO) * EONS_MAX_PNN);
  memset(m_dwBytes, 0, sizeof(DWORD) * EONS_MAX_BUF_LINES);
  memset(m_bBuffer, 0, EONS_MAX_BUF_BYTES * EONS_MAX_BUF_LINES);
  memset(m_szModuleLongOperatorName, 0, EONS_MAX_OPN_LEN + 1);
  memset(m_szModuleShortOperatorName, 0, EONS_MAX_OPN_LEN + 1);
  memset(m_szModuleServiceProviderName, 0, EONS_MAX_OPN_LEN + 1);

  if (!GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("UseServiceProviderName"), &m_dwUseServiceProvider))
    m_dwUseServiceProvider = 0;
  dwTmp = 1;
  (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("EONSMustReadIMSIbeforeReadingSIM"), &dwTmp);
  m_fMustReadIMSIbeforeReadingSIM = (dwTmp == 1 ? TRUE : FALSE);
  if (IsForbiddenSIMFile(28423))          // If IMSI is a forbidden file, the read IMSI workaround cannot be used.
    m_fMustReadIMSIbeforeReadingSIM = FALSE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Destructor
//
//////////////////////////////////////////////////////////////////////////////
CEonsHandling::~CEonsHandling() {
  (void)Deinit();
  if (m_hReadyEvent)
    (void)CloseHandle(m_hReadyEvent);
  DeleteCriticalSection(&m_csRunningCmd);
  DeleteCriticalSection(&m_csRunningReadTable);
}

//////////////////////////////////////////////////////////////////////////////
//
// Initialize and start the EONS handling if available.
//
// Params:
//  pRilHandle: Pointer to the connected CRilHandle instance which is needed
//              internally.
//
// Return:
//  TRUE or FALSE depending on the success of the operation. FALSE is returned
//  on any problem accessing the module, TRUE is returned if everything was ok
//  even if EONS is not available.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CEonsHandling::Init(CRilHandle* pRilDevice) {
    DWORD      dwTmp = 0;
    BOOL       fRet;

    if (!pRilDevice) {
        return FALSE;
    }

    m_pRilDevice = pRilDevice;

    fRet = FALSE;
    m_SupportType = EONS_DISABLED;
    m_fEONSTableAvailable = FALSE;
    // Check some input conditions
    if (m_pRilDevice==NULL || m_hReadyEvent==NULL)
        goto Error;

    if (!GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("UseServiceProviderName"), &m_dwUseServiceProvider))
        m_dwUseServiceProvider = 0;

    if (!GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("EONSMustReadIMSIbeforeReadingSIM"), &dwTmp))
        dwTmp = (READ_IMSI_BEFORE_CRSM ? 1 : 0);
    m_fMustReadIMSIbeforeReadingSIM = (dwTmp == 1 ? TRUE : FALSE);
    if (IsForbiddenSIMFile(28423))          // If IMSI is a forbidden file, the read IMSI workaround cannot be used.
        m_fMustReadIMSIbeforeReadingSIM = FALSE;

    ReadSIMServiceTable();

    // Check registry if EONS is requested
    dwTmp = 0;
    (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("EonsEnable"), &dwTmp);
    if (dwTmp != 1)
    {
        // Unblock ready event now, since there won't be any additional initialization.
        (void)SetEvent(m_hReadyEvent);
        // If EONS is not requested, we can stop here and return OK.
        fRet = TRUE;
        goto Error;
    }

    // The initialization may take up to 20 seconds. To avoid blocking the
    // rest of the RIL initialization, the rest of the initialization is done in
    // a thread in the background.
    (void)ResetEvent(m_hReadyEvent);
    CloseHandle(CreateThread(NULL, 0, InitializeEONSThread, (LPVOID)this, 0, 0));
    fRet = TRUE;

Error:
    return fRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Re-Init EONS tables (eg. after SIM file change)
//
// Return:
//  TRUE or FALSE depending on the success of the operation
//
//////////////////////////////////////////////////////////////////////////////
BOOL CEonsHandling::ReInit(void) {
    BOOL fRet = FALSE;

    if (!m_pRilDevice) {
        goto Error;
    }
    if (m_SupportType == EONS_DISABLED || m_fDisableEONSSIMReadTable == TRUE) {
        fRet = TRUE;
        goto Error;
    }

    // Until the re-runs of the EONS initialization has been completed, all current EONS settings are void.
    (void)ResetEvent(m_hReadyEvent);

    // Re-run the EONS initialization.
    CloseHandle(CreateThread(NULL, 0, InitializeEONSThread, (LPVOID)this, 0, 0));
    fRet = TRUE;

Error:
    return fRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Stop the EONS handling
//
// Return:
//  TRUE or FALSE depending on the success of the operation
//
//////////////////////////////////////////////////////////////////////////////
BOOL CEonsHandling::Deinit(void) {
  // Nothin to do for now
  m_pRilDevice = 0;
  return TRUE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Gets operator name from internanal EONS table (if available)
//
// Return:
//  TRUE or FALSE depending on the success of the operation
//
//////////////////////////////////////////////////////////////////////////////
BOOL CEonsHandling::GetOperatorInfoFromTable(WORD wNetworkCode, LPSTR szLongName, LPSTR szShortName, INT iBufferLen)
{
    BYTE pbMCC[3];
    BYTE pbMNC[3];
    BOOL fRet = FALSE;

    // Block call until we finished initializing
    if (m_fEONSTableAvailable && wNetworkCode >= 10000) {
      DWORD i;

      SYNCBLOCK(m_csRunningCmd);
      if (wNetworkCode > 99999) {
        m_wCurrentMCC = wNetworkCode / 1000;
        m_wCurrentMNC = wNetworkCode % 1000;
      } else {
        m_wCurrentMCC = wNetworkCode / 100;
        m_wCurrentMNC = wNetworkCode % 100;
      }
        pbMCC[0] = (BYTE)(m_wCurrentMCC / 100);
        pbMCC[1] = (BYTE)((m_wCurrentMCC / 10) % 10);
        pbMCC[2] = (BYTE)(m_wCurrentMCC % 10);
        if (m_wCurrentMNC >= 100) {
          pbMNC[0] = (BYTE)(m_wCurrentMNC / 100);
          pbMNC[1] = (BYTE)((m_wCurrentMNC / 10) % 10);
          pbMNC[2] = (BYTE)(m_wCurrentMNC % 10);
        }
        else {
          pbMNC[0] = (BYTE)(m_wCurrentMNC / 10);
          pbMNC[1] = (BYTE)(m_wCurrentMNC % 10);
          pbMNC[2] = 0x0f;
        }

        // Now try to find a corresponding entry in the OPL table
        for (i = 0; i < m_dwOplEntries; i++) {
          if (m_tOplInfo[i].fValid == TRUE) {
            if (((pbMCC[0] == m_tOplInfo[i].pbMCC[0]) || (m_tOplInfo[i].pbMCC[0] == 0x0d)) &&
                ((pbMCC[1] == m_tOplInfo[i].pbMCC[1]) || (m_tOplInfo[i].pbMCC[1] == 0x0d)) &&
                ((pbMCC[2] == m_tOplInfo[i].pbMCC[2]) || (m_tOplInfo[i].pbMCC[2] == 0x0d)) &&
                ((pbMNC[0] == m_tOplInfo[i].pbMNC[0]) || (m_tOplInfo[i].pbMNC[0] == 0x0d)) &&
                ((pbMNC[1] == m_tOplInfo[i].pbMNC[1]) || (m_tOplInfo[i].pbMNC[1] == 0x0d)) &&
                ((pbMNC[2] == m_tOplInfo[i].pbMNC[2]) || (m_tOplInfo[i].pbMNC[2] == 0x0d) || (IsUSAMCC(m_wCurrentMCC)  && (m_tOplInfo[i].pbMNC[2] == 0x00)))) {
              if (strlen(m_tPnnInfo[m_tOplInfo[i].bPLMN].szLongOperatorName) > 0 && szLongName != NULL) {
                (void)strncpyz(szLongName, m_tPnnInfo[m_tOplInfo[i].bPLMN].szLongOperatorName, iBufferLen);
              }
              if (strlen(m_tPnnInfo[m_tOplInfo[i].bPLMN].szShortOperatorName) > 0 && szShortName != NULL) {
                (void)strncpyz(szShortName, m_tPnnInfo[m_tOplInfo[i].bPLMN].szShortOperatorName, iBufferLen);
              }
              fRet = TRUE;
              break;
            }
          }
        }
    }

    return fRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// When using slow SIMs the RIL might not be able to get the current
// operator name. This function should be called to find out, if it is
// possible to query the opertor name with GetOperatorInfo().
//
// Return:
//   TRUE - RIL is ready to use GetOperatorInfo()
//   FALSE - GetOperatorInfo() is currently blocked
//
//////////////////////////////////////////////////////////////////////////////
BOOL CEonsHandling::IsEONSready(void) const {
BOOL fRet;

  fRet = (WaitForSingleObject(m_hReadyEvent, 0) == WAIT_OBJECT_0) ? TRUE : FALSE;
  return fRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Determine the EONS operator information
//
// Params:
//  szLongName : (Out) The long operator name if available
//  szShortName: (Out) The short operator name if available
//  iBufferLen: (In) The length of the two strings
//
// Return:
//  TRUE or FALSE depending on the success of the operation
//
//////////////////////////////////////////////////////////////////////////////
BOOL CEonsHandling::GetOperatorInfo(LPSTR szLongName, LPSTR szShortName, INT iBufferLen) {
DWORD  i;
BYTE   pbMCC[3];
BYTE   pbMNC[3];
BOOL   fRet;

  fRet = FALSE;
  // Block call until we finished initializing
  if (WaitForSingleObject(m_hReadyEvent, INFINITE) == WAIT_OBJECT_0) {
    // Check if EONS is emulated or is supported by module
    m_nEONSType = 0;
    if (m_SupportType == EONS_ENABLED) {
      // Place the command into the command queue (this may block if the queue is full)
      SYNCBLOCK(m_csRunningCmd);
      memset(m_szModuleServiceProviderName, 0, EONS_MAX_OPN_LEN + 1);
#ifdef SUPPORTS_EHSX_R1
      if (NO_SIND_INDICATIONS || AVOID_SIND_EONS) {
        if (QueueInternalCmdBlocking(HCCMD_STANDARD, m_pRilDevice, "AT+XCOPS=9\r", CMDOPT_NONE, APIID_SENDRESTRICTEDSIMCMD, 0, 0, 0, ParseXCOPSData, NULL, this)) {
          // Get the found operator name
          if (strlen(m_szModuleLongOperatorName) && m_nEONSType >= 2 && m_nEONSType <= 4) {
            (void)strncpyz(szLongName, m_szModuleLongOperatorName, iBufferLen);
            fRet = TRUE;
          }
          if (strlen(m_szModuleShortOperatorName) && m_nEONSType >= 2 && m_nEONSType <= 4) {
            (void)strncpyz(szShortName, m_szModuleShortOperatorName, iBufferLen);
            fRet = TRUE;
          }
        }
        // No EONS long name? Try the short name as well.
        if (strlen(szLongName) == 0) {
          if (QueueInternalCmdBlocking(HCCMD_STANDARD, m_pRilDevice, "AT+XCOPS=8\r", CMDOPT_NONE, APIID_SENDRESTRICTEDSIMCMD, 0, 0, 0, ParseXCOPSData, NULL, this)) {
            // Get the found operator name
            if (strlen(m_szModuleLongOperatorName) && m_nEONSType >= 2 && m_nEONSType <= 4) {
              (void)strncpyz(szLongName, m_szModuleLongOperatorName, iBufferLen);
              fRet = TRUE;
            }
            if (strlen(m_szModuleShortOperatorName) && m_nEONSType >= 2 && m_nEONSType <= 4) {
              (void)strncpyz(szShortName, m_szModuleShortOperatorName, iBufferLen);
              fRet = TRUE;
            }
          }
        }
      }
      else {
#endif
        DWORD dwRetries = 0;
        DWORD dwRetryDelay = 0;

        if (NEED_SIND_EONS_RETRIES) {
          // Problem with EHS5: AT^SIND=eons might return with an error, if the
          // protocol stack is curently busy.
          if (!GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("EHS5SINDEONSRetries"), &dwRetries))
            dwRetries = 10;
          if (!GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("EHS5SINDEONSRetryDelay"), &dwRetryDelay))
            dwRetryDelay = 1000;
        }
        // Bug in EHS5: AT^SIND=eons,2 is only working properly when module is set to GSM.
        if (QueueInternalCmdBlocking(HCCMD_STANDARD, m_pRilDevice, "AT^SIND=eons,2\r", CMDOPT_NONE, APIID_SENDRESTRICTEDSIMCMD, 0, dwRetries, dwRetryDelay, ParseEONSData, NULL, this)) {
          // Get the found operator name
          if (strlen(m_szModuleLongOperatorName)) {
            (void)strncpyz(szLongName, m_szModuleLongOperatorName, iBufferLen);
            fRet = TRUE;
          }
          if (strlen(m_szModuleShortOperatorName)) {
            (void)strncpyz(szShortName, m_szModuleShortOperatorName, iBufferLen);
            fRet = TRUE;
          }
        }
#ifdef SUPPORTS_EHSX_R1
      }
#endif

      if (fRet == TRUE) {
        RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: EONS module support -> Operator found: \"%a\" - \"%a\"\r\n"), m_szModuleLongOperatorName, m_szModuleShortOperatorName));
      } else {
        RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: EONS module support -> Operator not found\r\n")));
      }
    }
    // The RIL must emulator EONS if the module doesn't support EONS at all, or if
    // the current EONS type is 1 (= EONS SIM files) and the module has the bug
    // of not reading the SIM files correctly.
    if (m_SupportType == EONS_EMULATED || (m_nEONSType == 1 && MUST_CHECK_SIM_FOR_EONS && m_fEONSTableAvailable)) {
      // Place the command into the command queue (this may block if the queue is full)
      SYNCBLOCK(m_csRunningCmd);
      m_dwLines = 0;
      if (!SMONI_UNAVAILABLE && QueueInternalCmdBlocking(HCCMD_STANDARD, m_pRilDevice, "AT^SMONI\r", CMDOPT_NONE, APIID_SENDRESTRICTEDSIMCMD, 0, 0, 0, ParseSmoniData, NULL, this)) {
        // Get the BCD codes of MCC and MNC
        pbMCC[0] = (BYTE)(m_wCurrentMCC / 100);
        pbMCC[1] = (BYTE)((m_wCurrentMCC / 10) % 10);
        pbMCC[2] = (BYTE)(m_wCurrentMCC % 10);
        if (m_wCurrentMNC >= 100) {
          pbMNC[0] = (BYTE)(m_wCurrentMNC / 100);
          pbMNC[1] = (BYTE)((m_wCurrentMNC / 10) % 10);
          pbMNC[2] = (BYTE)(m_wCurrentMNC % 10);
        }
        else {
          pbMNC[0] = (BYTE)(m_wCurrentMNC / 10);
          pbMNC[1] = (BYTE)(m_wCurrentMNC % 10);
          pbMNC[2] = 0x0f;
        }

        // Now try to find a corresponding entry in the OPL table
        for (i = 0; i < m_dwOplEntries; i++) {
          if (m_tOplInfo[i].fValid == TRUE) {
            if (((pbMCC[0] == m_tOplInfo[i].pbMCC[0]) || (m_tOplInfo[i].pbMCC[0] == 0x0d)) &&
                ((pbMCC[1] == m_tOplInfo[i].pbMCC[1]) || (m_tOplInfo[i].pbMCC[1] == 0x0d)) &&
                ((pbMCC[2] == m_tOplInfo[i].pbMCC[2]) || (m_tOplInfo[i].pbMCC[2] == 0x0d)) &&
                ((pbMNC[0] == m_tOplInfo[i].pbMNC[0]) || (m_tOplInfo[i].pbMNC[0] == 0x0d)) &&
                ((pbMNC[1] == m_tOplInfo[i].pbMNC[1]) || (m_tOplInfo[i].pbMNC[1] == 0x0d)) &&
                ((pbMNC[2] == m_tOplInfo[i].pbMNC[2]) || (m_tOplInfo[i].pbMNC[2] == 0x0d) || (IsUSAMCC(m_wCurrentMCC) && (m_tOplInfo[i].pbMNC[2] == 0x00))) &&
                (m_wCurrentLAC >= m_tOplInfo[i].wLACLow) && (m_wCurrentLAC <= m_tOplInfo[i].wLACHigh)) {
              if (strlen(m_tPnnInfo[m_tOplInfo[i].bPLMN].szLongOperatorName)) {
                (void)strncpyz(szLongName, m_tPnnInfo[m_tOplInfo[i].bPLMN].szLongOperatorName, iBufferLen);
              }
              if (strlen(m_tPnnInfo[m_tOplInfo[i].bPLMN].szShortOperatorName)) {
                (void)strncpyz(szShortName, m_tPnnInfo[m_tOplInfo[i].bPLMN].szShortOperatorName, iBufferLen);
              }
              fRet = TRUE;
              break;
            }
          }
        }
        if ((UseServiceProvider() == USE_SERVICE_PROVDER_NAME_ON || UseServiceProvider() == USE_SERVICE_PROVDER_NAME_ON_NAMEONLY) && strlen(m_szModuleServiceProviderName) > 0) {
          // Use service provider name instead of operator name
          (void)_snprintfz(szLongName, EONS_MAX_OPN_LEN, "%s", m_szModuleServiceProviderName);
          (void)_snprintfz(szShortName, EONS_MAX_OPN_LEN, "%s", m_szModuleServiceProviderName);
        }
        else if (UseServiceProvider() == USE_SERVICE_PROVDER_NAME_COMBINATION && strlen(m_szModuleServiceProviderName) > 0) {
          // Use both service provider name and operator name.
          // The operator name will be according of the modules name table, regardless
          // of the setting of USE_MODULE_TABLE_OF_OPERATORS.
          (void)_snprintfz(szLongName, EONS_MAX_OPN_LEN, "%s %s", m_tPnnInfo[m_tOplInfo[i].bPLMN].szLongOperatorName, m_szModuleServiceProviderName);
          (void)_snprintfz(szShortName, EONS_MAX_OPN_LEN, "%s %s", m_tPnnInfo[m_tOplInfo[i].bPLMN].szShortOperatorName, m_szModuleServiceProviderName);
        }
        if (fRet == TRUE) {
          RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: EONS emulation -> Operator found: \"%a\" - \"%a\"\r\n"), szLongName, szShortName));
        } else {
          RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: EONS emulation -> Operator not found\r\n")));
        }
      }
    }
  }

  return fRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Private implementation
//
//////////////////////////////////////////////////////////////////////////////

// Error translation: the HC25 returns 'invalid index' if the SIM is busy
const CME_TRANSLATION g_CMET_EONS_CRSM_INVALID_INDEX[] = {{RIL_E_INVALIDINDEX, RIL_E_SIMBUSY},{RIL_E_SIMFAILURE, RIL_E_SIMNOTINSERTED},{NULL,NULL}};

//////////////////////////////////////////////////////////////////////////////
//
// Check if EONS module handling returns the long or the short name for the
// provider name types 2 and 3
//
// Return:
//  TRUE if the module returns the loing name, FALSE if the short name is returned
//
//////////////////////////////////////////////////////////////////////////////
BOOL CEonsHandling::IsLongName(void) {
BOOL fRet = FALSE;

  // Place the command into the command queue (this may block if the queue is full)
  SYNCBLOCK(m_csRunningCmd);
  m_dwLines = 0;
  m_wSimSw1 = 0;
  m_wSimSw2 = 0;
  if (!IsForbiddenSIMFile(EF_OPR)) {
    char szCmd[MAX_ATCMD_LEN];
    char szPath[MAX_PATH];
    DWORD dwRetry = 0;

    if (!GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("RetriesForEONSLongName"), &dwRetry))
        dwRetry = DEFAULT_RETRIES_FOR_CRSM_LONGNAME;

    if (m_fMustReadIMSIbeforeReadingSIM)
      (void)strncpyz(szCmd, "AT+CRSM=192,28423,0,0,15;+CRSM=192,28440,0,0,25", MAX_ATCMD_LEN);
    else
      (void)strncpyz(szCmd, "AT+CRSM=192,28440,0,0,25", MAX_ATCMD_LEN);
    if (NeedSIMPath(m_pRilDevice->GetCurrentSIMType(), 28440, szPath, MAX_PATH)) {
      (void)strncat(szCmd, ",,\"", MAX_ATCMD_LEN - strlen(szCmd));
      (void)strncat(szCmd, szPath, MAX_ATCMD_LEN - strlen(szCmd));
      (void)strncat(szCmd, "\"", MAX_ATCMD_LEN - strlen(szCmd));
    }
    (void)strncat(szCmd, "\r", MAX_ATCMD_LEN - strlen(szCmd));
    if (QueueInternalCmdBlocking(HCCMD_STANDARD, m_pRilDevice, szCmd, CMDOPT_RETRYONINVALIDINDEX, APIID_SENDRESTRICTEDSIMCMD, 0, dwRetry, DEFAULT_RETRY_DELAY_FOR_CRSM, ParseSimData, NULL, this, NULL, g_CMET_EONS_CRSM_INVALID_INDEX)) {  // access EF_OPR field
      if ((m_wSimSw1 == 0x94) && (m_wSimSw2 == 0x04)) {
        // SIM doesn't support short EONS names
        fRet = TRUE;
      }
    }
  }
  return fRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Try to enable module internal EONS handling
//
// Return:
//  TRUE or FALSE depending on the success of the operation
//
//////////////////////////////////////////////////////////////////////////////
BOOL CEonsHandling::EnableModuleHandling(void) {
BOOL fRet = FALSE;

  // EHS5 Rel1: EONS is always enabled, so there is no need to send an AT command.
  if (CHECK_EONS_AFTER_CELL_CHANGE)
    return TRUE;

  SYNCBLOCK(m_csRunningCmd);
  // Module supports EONS -> Enable the URC
  if (QueueInternalCmdBlocking(HCCMD_STANDARD, m_pRilDevice, "AT^SIND=eons,1\r", CMDOPT_NONE, APIID_SENDRESTRICTEDSIMCMD, 0, 0, 0)) {
    fRet = TRUE;
  }

  return fRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Get info about SIM file record length and number of records from
// SIM response stored in m_bBuffer[0].
// This functionm is for linear SIM files.
//
// Return:
//  TRUE or FALSE depending on the success of the operation
//
//////////////////////////////////////////////////////////////////////////////
BOOL CEonsHandling::GetSIMFIleInfo(SIMTYPE iSIMType, INT &iRecordLen, INT &iNumRecords) const
{
  BOOL bSuccess = TRUE;
  BOOL bUnknownRecordType = TRUE;

  if (iSIMType == SIMTYPE_3G_SIM) {
    UINT uiPointer = 2;

    iNumRecords = 0;
    while (uiPointer+1 < m_dwBytes[0])
    {
      if (uiPointer+m_bBuffer[0][uiPointer+1] > m_dwBytes[0])
        break;

      switch (m_bBuffer[0][uiPointer])
      {
        case 0x82:
        {
          switch (m_bBuffer[0][uiPointer+2] & 0x7)
          {
            case 2:
              iNumRecords = m_bBuffer[0][uiPointer+6];
              iRecordLen = m_bBuffer[0][uiPointer+5] + (m_bBuffer[0][uiPointer+4] * 256);
              bUnknownRecordType = FALSE;
              break;
          }
        }
        break;
      }
      uiPointer += (2+m_bBuffer[0][uiPointer+1]);
    }
  }
  // 2G-SIM detected, decode in 2G-format.
  // Also try to decode in 2G format if the SIM is supposed to be a 3G SIM, but the NEED_RECHECK_2G3G_SIM
  // workaround is needed.
  if ((iSIMType != SIMTYPE_3G_SIM) || (NEED_RECHECK_2G3G_SIM && bUnknownRecordType))
  {
      if (m_dwLines == 1 && m_dwBytes[0] >= 15) {
        iRecordLen = (INT)m_bBuffer[0][14];
        if (iRecordLen > 0)
          iNumRecords = (INT)((m_bBuffer[0][2] << 8) | m_bBuffer[0][3]) / (INT)m_bBuffer[0][14];
        else
          iNumRecords = 0;
      }
      else {
        bSuccess = FALSE;
      }
  }

  return bSuccess;
}

//////////////////////////////////////////////////////////////////////////////
//
// Get info about SIM file length from SIM response stored in m_bBuffer[0].
// This functionm is for transparent SIM files.
//
// Return:
//  TRUE or FALSE depending on the success of the operation
//
//////////////////////////////////////////////////////////////////////////////
BOOL CEonsHandling::GetSIMFIleInfo(SIMTYPE iSIMType, INT &iFileLen) const
{
  BOOL bSuccess = TRUE;
  BOOL bUnknownRecordType = TRUE;

  if (iSIMType == SIMTYPE_3G_SIM) {
    UINT uiPointer = 2;

    while (uiPointer+1 < m_dwBytes[0])
    {
      if (uiPointer+(2+m_bBuffer[0][uiPointer+1]) > m_dwBytes[0])
        break;

      switch (m_bBuffer[0][uiPointer])
      {
        case 0x80:
        {
          iFileLen = (INT)(m_bBuffer[0][uiPointer+2] * 256) + (INT)m_bBuffer[0][uiPointer+3];
          bUnknownRecordType = FALSE;
        }
        break;
      }
      uiPointer += (2+m_bBuffer[0][uiPointer+1]);
    }
  }
  // 2G-SIM detected, decode in 2G-format.
  // Also try to decode in 2G format if the SIM is supposed to be a 3G SIM, but the NEED_RECHECK_2G3G_SIM
  // workaround is needed.
  if ((iSIMType != SIMTYPE_3G_SIM) || (NEED_RECHECK_2G3G_SIM && bUnknownRecordType))
  {
      if (m_dwLines == 1 && m_dwBytes[0] >= 15) {
        iFileLen = (INT)((m_bBuffer[0][2] << 8) | m_bBuffer[0][3]);
      }
      else {
        bSuccess = FALSE;
      }
  }

  return bSuccess;
}

//////////////////////////////////////////////////////////////////////////////
//
// Load the EF-PNN table from the SIM card
//
// Return:
//  TRUE or FALSE depending on the success of the operation
//
//////////////////////////////////////////////////////////////////////////////
INT CEonsHandling::LoadPnnTable(void) {
DWORD  dwRec;
DWORD  dwCurrRecs;
INT    iRet;
INT    iRecordLen = 24;
INT    iNumRecords = 0;
char   szCmd[MAX_ATCMD_LEN];
DWORD dwRetry = 0;

  if (!GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("RetriesForEONS"), &dwRetry))
    dwRetry = DEFAULT_RETRIES_FOR_CRSM;
  m_dwPnnEntries = 0;
  memset(m_tPnnInfo, 0, sizeof(PNN_INFO) * EONS_MAX_PNN);
  iRet = READ_EONS_SIM_OK;
  dwRec = 0;
  if (!IsForbiddenSIMFile(EF_PNN)) {
      char szPath[MAX_PATH];

      if (m_fMustReadIMSIbeforeReadingSIM)
        (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CRSM=192,28423,0,0,15;+CRSM=192,%d,0,0,15", EF_PNN);
      else
        (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CRSM=192,%d,0,0,15", EF_PNN);
      if (NeedSIMPath(m_pRilDevice->GetCurrentSIMType(), EF_PNN, szPath, MAX_PATH)) {
        (void)strncat(szCmd, ",,\"", MAX_ATCMD_LEN - strlen(szCmd));
        (void)strncat(szCmd, szPath, MAX_ATCMD_LEN - strlen(szCmd));
        (void)strncat(szCmd, "\"", MAX_ATCMD_LEN - strlen(szCmd));
      }
      (void)strncat(szCmd, "\r", MAX_ATCMD_LEN - strlen(szCmd));
      m_dwLines = 0;
      if (!QueueInternalCmdBlocking(HCCMD_STANDARD, m_pRilDevice, szCmd, CMDOPT_RETRYONINVALIDINDEX, APIID_SENDRESTRICTEDSIMCMD, 0, dwRetry, DEFAULT_RETRY_DELAY_FOR_CRSM, ParseSimData, NULL, this))
        return READ_EONS_SIM_MODULE_FAILURE;
      if (!GetSIMFIleInfo(m_pRilDevice->GetCurrentSIMType() , iRecordLen, iNumRecords))
        return READ_EONS_SIM_MODULE_FAILURE;

      for (;;) {
        dwCurrRecs = GenerateATCmd(szCmd, MAX_ATCMD_LEN_EONS, EF_PNN, dwRec + 1, iRecordLen, min(EONS_MAX_PNN,iNumRecords) - dwRec);
        dwRec += dwCurrRecs;

        // Place the command into the command queue (this may block if the queue is full)
        SYNCBLOCK(m_csRunningCmd);
        m_dwLines = 0;
        if (!QueueInternalCmdBlocking(HCCMD_STANDARD, m_pRilDevice, szCmd, CMDOPT_RETRYONINVALIDINDEX, APIID_SENDRESTRICTEDSIMCMD, 0, dwRetry, DEFAULT_RETRY_DELAY_FOR_CRSM, ParseSimData, NULL, this, NULL, g_CMET_EONS_CRSM_INVALID_INDEX)) {
          iRet = READ_EONS_SIM_MODULE_FAILURE;
          break;
        }

        // Scan the loaded SIM PNN data and copy it into our internal table
        if (!ScanPnnTable()) {
          iRet = READ_EONS_SIM_INVALID;
          break;
        }

        // Check if our table is full or there are no more records
        if ((dwRec >= (DWORD)min(EONS_MAX_PNN,iNumRecords)) || (m_dwLines < dwCurrRecs)) {
          break;
        }
      }
  }

  return iRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Load the EF-OPL table from the SIM card
//
// Return:
//  TRUE or FALSE depending on the success of the operation
//
//////////////////////////////////////////////////////////////////////////////
INT CEonsHandling::LoadOplTable(void) {
DWORD  dwRec;
DWORD  dwCurrRecs;
INT   iRet;
INT   iNumRecords = 0;
char   szCmd[MAX_ATCMD_LEN];
DWORD dwRetry = 0;
INT iRecLen = 0;

  if (!GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("RetriesForEONS"), &dwRetry))
    dwRetry = DEFAULT_RETRIES_FOR_CRSM;
  iRet = READ_EONS_SIM_INVALID;
  m_dwOplEntries = 0;
  memset(m_tOplInfo, 0, sizeof(OPL_INFO) * EONS_MAX_OPL);
  // Check if the required OPL table has been successfully loaded
  if (m_dwPnnEntries && !IsForbiddenSIMFile(EF_OPL)) {
    char szPath[MAX_PATH];

    iRet = READ_EONS_SIM_OK;
    dwRec = 0;
    if (m_fMustReadIMSIbeforeReadingSIM)
      (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CRSM=192,28423,0,0,15;+CRSM=192,%d,0,0,15", EF_OPL);
    else
      (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CRSM=192,%d,0,0,15", EF_OPL);
    if (NeedSIMPath(m_pRilDevice->GetCurrentSIMType(), EF_OPL, szPath, MAX_PATH)) {
      (void)strncat(szCmd, ",,\"", MAX_ATCMD_LEN - strlen(szCmd));
      (void)strncat(szCmd, szPath, MAX_ATCMD_LEN - strlen(szCmd));
      (void)strncat(szCmd, "\"", MAX_ATCMD_LEN - strlen(szCmd));
    }
    (void)strncat(szCmd, "\r", MAX_ATCMD_LEN - strlen(szCmd));
    m_dwLines = 0;
    if (!QueueInternalCmdBlocking(HCCMD_STANDARD, m_pRilDevice, szCmd, CMDOPT_RETRYONINVALIDINDEX, APIID_SENDRESTRICTEDSIMCMD, 0, dwRetry, DEFAULT_RETRY_DELAY_FOR_CRSM, ParseSimData, NULL, this))
      return READ_EONS_SIM_MODULE_FAILURE;

    if (!GetSIMFIleInfo(m_pRilDevice->GetCurrentSIMType() , iRecLen, iNumRecords))
      return READ_EONS_SIM_MODULE_FAILURE;

    for (;;) {
      dwCurrRecs = GenerateATCmd(szCmd, MAX_ATCMD_LEN_EONS, EF_OPL, dwRec + 1, (DWORD)iRecLen, min(EONS_MAX_OPL,iNumRecords) - dwRec);
      dwRec += dwCurrRecs;
      // Place the command into the command queue (this may block if the queue is full)
      SYNCBLOCK(m_csRunningCmd);
      m_dwLines = 0;
      if (!QueueInternalCmdBlocking(HCCMD_STANDARD, m_pRilDevice, szCmd, CMDOPT_RETRYONINVALIDINDEX, APIID_SENDRESTRICTEDSIMCMD, 0, dwRetry, DEFAULT_RETRY_DELAY_FOR_CRSM, ParseSimData, NULL, this, NULL, g_CMET_EONS_CRSM_INVALID_INDEX)) {
        iRet = READ_EONS_SIM_MODULE_FAILURE;
        break;
      }

      // Scan the loaded SIM PNN data and copy it into our internal table
      if (!ScanOplTable()) {
        iRet = READ_EONS_SIM_INVALID;
        break;
      }

      // Check if our table is full or there are no more records
      if ((dwRec >= (DWORD)min(EONS_MAX_OPL,iNumRecords)) || (m_dwLines < dwCurrRecs)) {
        break;
      }
    }
  }

  return iRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Scan the loaded EF-PNN data into our internal table
//
// Return:
//  TRUE or FALSE depending on the success of the operation
//
//////////////////////////////////////////////////////////////////////////////
BOOL CEonsHandling::ScanPnnTable(void) {
DWORD   i;
DWORD   dwLen;
DWORD   dwBytes;
DWORD   dwBytes2;
BOOL    fRet;
BOOL    fLongNameFound;
BOOL    fShortNameFound;

  fRet = TRUE;
  for (i = 0; i < m_dwLines; i++) {
    if (m_dwPnnEntries >= EONS_MAX_PNN) {
      fRet = FALSE;
      break;
    }
    if (m_dwBytes[i] >= EONS_MAX_OPN_LEN) {
      dwBytes = m_bBuffer[i][1] - 1;
      if (dwBytes <= (EONS_MAX_OPN_LEN - 3)) {
        fLongNameFound = FALSE;
        fShortNameFound = FALSE;
        if ((m_bBuffer[i][2] & PNN_CODING_MASK) == PNN_CODING_GSM) {
          // Long name is 7 bit GSM coded
          dwLen = ((dwBytes * 8) - (m_bBuffer[i][2] & 0x7)) / 7;
          if (ScanPnnName(m_bBuffer[i] + 3, m_tPnnInfo[m_dwPnnEntries].szLongOperatorName, dwLen)) {
            fLongNameFound = TRUE;
          }
        }
        if (dwBytes < (EONS_MAX_BUF_BYTES - 6)) {
          dwBytes2 = m_bBuffer[i][3 + dwBytes + 1] - 1;
          if (dwBytes2 <= ((EONS_MAX_OPN_LEN - 6) - dwBytes)) {
            if ((m_bBuffer[i][3 + dwBytes + 2] & PNN_CODING_MASK) == PNN_CODING_GSM) {
              // Short name is 7 bit GSM coded
              dwLen = ((dwBytes2 * 8) - (m_bBuffer[i][3 + dwBytes + 2] & 0x7)) / 7;
              if (ScanPnnName(m_bBuffer[i] + 6 + dwBytes, m_tPnnInfo[m_dwPnnEntries].szShortOperatorName, dwLen)) {
                fShortNameFound = TRUE;
              }
            }
          }
        }
        if (!fLongNameFound && !fShortNameFound) {
          // No GSM coded eons name found!
          // Because the RIL API doesn't support UCS2 coded operator names,
          // we return FALSE which causes the class to think that the SIM doesn't support eons.
          fRet = FALSE;
        }
      }
      m_dwPnnEntries++;
    } else {
      fRet = FALSE;
    }
  }

  return fRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Scan the loaded EF-OPL data into our internal table
//
// Return:
//  TRUE or FALSE depending on the success of the operation
//
//////////////////////////////////////////////////////////////////////////////
BOOL CEonsHandling::ScanOplTable(void) {
DWORD  i;
BOOL   fRet;

  fRet = TRUE;
  for (i = 0; i < m_dwLines; i++) {
    if (m_dwOplEntries >= EONS_MAX_OPL) {
      fRet = FALSE;
      break;
    }
    if (m_dwBytes[i] < 8) {
      // In case the record is not not complete, fill it with default values.
      // This is necessary on SIM cards, which have only one network name
      // for all regions.
      DWORD j;
      const BYTE bDefOpl[] = { 0xDD, 0xDD, 0xDD, 0xDD, 0x00, 0x00, 0xFF, 0xFF};

      for (j = m_dwBytes[i]; j < 8; j++)
        m_bBuffer[i][j] = bDefOpl[j];
    }
    if (m_dwBytes[i] == 8) {
      m_tOplInfo[m_dwOplEntries].fValid    = TRUE;
      m_tOplInfo[m_dwOplEntries].pbMCC[0]  = m_bBuffer[i][0] & 0x0f;
      m_tOplInfo[m_dwOplEntries].pbMCC[1]  = m_bBuffer[i][0] >> 4;
      m_tOplInfo[m_dwOplEntries].pbMCC[2]  = m_bBuffer[i][1] & 0x0f;
      m_tOplInfo[m_dwOplEntries].pbMNC[0]  = m_bBuffer[i][2] & 0x0f;
      m_tOplInfo[m_dwOplEntries].pbMNC[1]  = m_bBuffer[i][2] >> 4;
      m_tOplInfo[m_dwOplEntries].pbMNC[2]  = m_bBuffer[i][1] >> 4;
      m_tOplInfo[m_dwOplEntries].wLACLow   = ((WORD)m_bBuffer[i][3] * 256) + (WORD)m_bBuffer[i][4];
      m_tOplInfo[m_dwOplEntries].wLACHigh  = ((WORD)m_bBuffer[i][5] * 256) + (WORD)m_bBuffer[i][6];
      m_tOplInfo[m_dwOplEntries].bPLMN     = m_bBuffer[i][7];
      // Check if entry is valid
      if ((m_tOplInfo[m_dwOplEntries].pbMCC[0] > 9) && (m_tOplInfo[m_dwOplEntries].pbMCC[0] != 0x0d)) {
        m_tOplInfo[m_dwOplEntries].fValid = FALSE;
      }
      if ((m_tOplInfo[m_dwOplEntries].pbMCC[1] > 9) && (m_tOplInfo[m_dwOplEntries].pbMCC[1] != 0x0d)) {
        m_tOplInfo[m_dwOplEntries].fValid = FALSE;
      }
      if ((m_tOplInfo[m_dwOplEntries].pbMCC[2] > 9) && (m_tOplInfo[m_dwOplEntries].pbMCC[2] != 0x0d)) {
        m_tOplInfo[m_dwOplEntries].fValid = FALSE;
      }
      if ((m_tOplInfo[m_dwOplEntries].pbMNC[0] > 9) && (m_tOplInfo[m_dwOplEntries].pbMNC[0] != 0x0d)) {
        m_tOplInfo[m_dwOplEntries].fValid = FALSE;
      }
      if ((m_tOplInfo[m_dwOplEntries].pbMNC[1] > 9) && (m_tOplInfo[m_dwOplEntries].pbMNC[1] != 0x0d)) {
        m_tOplInfo[m_dwOplEntries].fValid = FALSE;
      }
      if ((m_tOplInfo[m_dwOplEntries].pbMNC[2] > 9) && (m_tOplInfo[m_dwOplEntries].pbMNC[2] != 0x0d) && (m_tOplInfo[m_dwOplEntries].pbMNC[2] != 0x0f)) {
        m_tOplInfo[m_dwOplEntries].fValid = FALSE;
      }
      if (m_tOplInfo[m_dwOplEntries].wLACLow > m_tOplInfo[m_dwOplEntries].wLACHigh) {
        m_tOplInfo[m_dwOplEntries].fValid = FALSE;
      }
      if (m_tOplInfo[m_dwOplEntries].bPLMN < 1) {
        m_tOplInfo[m_dwOplEntries].fValid = FALSE;
      } else if (m_tOplInfo[m_dwOplEntries].bPLMN >= EONS_MAX_OPL) {
        m_tOplInfo[m_dwOplEntries].fValid = FALSE;
      } else {
        m_tOplInfo[m_dwOplEntries].bPLMN--;
        if (!strlen(m_tPnnInfo[m_tOplInfo[m_dwOplEntries].bPLMN].szLongOperatorName)) {
          m_tOplInfo[m_dwOplEntries].fValid = FALSE;
        }
      }
      m_dwOplEntries++;
    } else {
      fRet = FALSE;
    }
  }

  return fRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Generate the linked AT+CRSM command
//
// Params:
//  szBuf     : Buffer for AT command (size has to be MAX_ATCMD_LEN)
//  dwAddr    : Address of SIM file
//  dwStartRec: Record where the AT command list is started
//  dwLength  : Length of records to read
//  dwMaxRecs : Maximal number of records to read
//
// Return:
//  The number of added +CRSM commands
//
//////////////////////////////////////////////////////////////////////////////
DWORD CEonsHandling::GenerateATCmd(char* szBuf, DWORD dwBufLen, DWORD dwAddr, DWORD dwStartRec, DWORD dwLength, DWORD dwMaxRecs) const {
char   szTmpCmd[MAX_ATCMD_LEN];
char   szPath[MAX_PATH];
DWORD  dwRet;
DWORD dwCmdLen;
DWORD dwTmpLen;

  dwRet = 0;
  if (m_fMustReadIMSIbeforeReadingSIM)
      (void)strncpyz(szBuf, "AT+CRSM=192,28423,0,0,15;", dwBufLen);
  else
      (void)strncpyz(szBuf, "AT", dwBufLen);
  dwCmdLen = strlen(szBuf);
  for (;;) {
    (void)_snprintfz(szTmpCmd, MAX_ATCMD_LEN, "+CRSM=178,%i,%i,4,%i", dwAddr, dwStartRec, dwLength);
    if (NeedSIMPath(m_pRilDevice->GetCurrentSIMType(), dwAddr, szPath, MAX_PATH)) {
      (void)strncat(szTmpCmd, ",,\"", MAX_ATCMD_LEN - strlen(szTmpCmd));
      (void)strncat(szTmpCmd, szPath, MAX_ATCMD_LEN - strlen(szTmpCmd));
      (void)strncat(szTmpCmd, "\"", MAX_ATCMD_LEN - strlen(szTmpCmd));
    }
    (void)strncat(szTmpCmd, ";", MAX_ATCMD_LEN - strlen(szTmpCmd));
    dwTmpLen = strlen(szTmpCmd);
    if (((dwCmdLen + dwTmpLen) >= (dwBufLen - 1)) || (dwRet >= dwMaxRecs)) {
      szBuf[dwCmdLen - 1] = '\r';
      szBuf[dwCmdLen] = '\0';
      break;
    }
    (void)strncatz(szBuf, szTmpCmd, dwBufLen);
    dwCmdLen += dwTmpLen;
    dwStartRec++;
    dwRet++;
  }

  return dwRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Scan the 7 bit operator name from the PNN table and convert it into a
// normal 8 bit string
//
// Params:
//  pSrc : Pointer to source in PNN table
//  pDst : Pointer to destination for transferred string
//  dwLen: Number of characters in operator name
//
// Return:
//  TRUE or FALSE depending on the success of the operation
//
//////////////////////////////////////////////////////////////////////////////
BOOL CEonsHandling::ScanPnnName(const BYTE* pSrc, char* pDst, DWORD dwLen) const {
DWORD   i;
DWORD   dwShift;
BYTE    bBuf[EONS_MAX_OPN_LEN + 1];
WCHAR   wszName[EONS_MAX_OPN_LEN + 1];
BOOL    fRet;

  fRet = FALSE;
  memset(bBuf, 0, sizeof(bBuf));
  dwShift = 0;
  for (i = 0; i < dwLen; i++) {
    switch(i % 8) {
      case 0:
        bBuf[i] = (BYTE)(pSrc[0 + (dwShift * 7)]);
        break;
      case 1:
        bBuf[i] = (BYTE)((pSrc[1 + (dwShift * 7)] << 1) | ((pSrc[0 + (dwShift * 7)] >> 7) & 0x01));
        break;
      case 2:
        bBuf[i] = (BYTE)((pSrc[2 + (dwShift * 7)] << 2) | ((pSrc[1 + (dwShift * 7)] >> 6) & 0x03));
        break;
      case 3:
        bBuf[i] = (BYTE)((pSrc[3 + (dwShift * 7)] << 3) | ((pSrc[2 + (dwShift * 7)] >> 5) & 0x07));
        break;
      case 4:
        bBuf[i] = (BYTE)((pSrc[4 + (dwShift * 7)] << 4) | ((pSrc[3 + (dwShift * 7)] >> 4) & 0x0f));
        break;
      case 5:
        bBuf[i] = (BYTE)((pSrc[5 + (dwShift * 7)] << 5) | ((pSrc[4 + (dwShift * 7)] >> 3) & 0x1f));
        break;
      case 6:
        bBuf[i] = (BYTE)((pSrc[6 + (dwShift * 7)] << 6) | ((pSrc[5 + (dwShift * 7)] >> 2) & 0x3f));
        break;
      case 7:
        bBuf[i] = (BYTE)(pSrc[6 + (dwShift * 7)] >> 1);
        dwShift++;
        break;
      default:
        bBuf[i] = 0;
        break;
    }
    bBuf[i] = (BYTE)(bBuf[i] & 0x7f);
  }

  if (ParseEncodedString(ENCODING_GSMDEFAULT, (char*)bBuf, (char*)(bBuf + dwLen), wszName, wszName + (EONS_MAX_OPN_LEN + 1))) {
    for (i = 0; i < wcslen(wszName); i++) {
      pDst[i] = (char)wszName[i];
    }
    fRet = TRUE;
  }

  return fRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Trace some useful EONS debug info
//
//////////////////////////////////////////////////////////////////////////////
void CEonsHandling::TraceEonsInfo(void) const {
#if defined(RIL_TRACE_REQUIRED) || defined (DEBUG)
    DWORD i;

    if (m_SupportType == EONS_DISABLED) {
      RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: EONS not enabled\r\n")));
    }
    if (m_SupportType == EONS_NOT_AVAILABLE) {
      RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: EONS not available\r\n")));
    }
    if (m_SupportType == EONS_ENABLED)
    {
        if (MUST_CHECK_SIM_FOR_EONS && !m_fEONSTableAvailable) {
            RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: EONS enabled (supported by module, no EONS SIM files)\r\n")));
        }
        else {
            RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: EONS enabled (supported by module)\r\n")));
        }
    }
    if (m_SupportType == EONS_EMULATED)
      RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: EONS enabled (not supported by module -> emulated)\r\n")));
    if (m_SupportType == EONS_EMULATED || (m_SupportType == EONS_ENABLED && MUST_CHECK_SIM_FOR_EONS && m_fEONSTableAvailable))
    {
      RILRetailTraceZone(ZONE_INFO, (TEXT("        -----------------------------------------------------\r\n")));
      RILRetailTraceZone(ZONE_INFO, (TEXT("        MCC  MNC  LACLO  LACHI  Names (Long <-> Short)\r\n")));
      RILRetailTraceZone(ZONE_INFO, (TEXT("        -----------------------------------------------------\r\n")));
      for (i = 0; i < m_dwOplEntries; i++) {
        if (m_tOplInfo[i].fValid == TRUE) {
          RILRetailTraceZone(ZONE_INFO, (TEXT("   %2d   %x%x%x  %x%x%x  %5d  %5d  \"%a\" <-> \"%a\"\r\n"), i,
                      m_tOplInfo[i].pbMCC[0], m_tOplInfo[i].pbMCC[1], m_tOplInfo[i].pbMCC[2],
                      m_tOplInfo[i].pbMNC[0], m_tOplInfo[i].pbMNC[1], m_tOplInfo[i].pbMNC[2],
                      m_tOplInfo[i].wLACLow, m_tOplInfo[i].wLACHigh,
                      m_tPnnInfo[m_tOplInfo[i].bPLMN].szLongOperatorName,
                      m_tPnnInfo[m_tOplInfo[i].bPLMN].szShortOperatorName));
        }
      }
      RILRetailTraceZone(ZONE_INFO, (TEXT("        -----------------------------------------------------\r\n")));
    }
#endif
}

//////////////////////////////////////////////////////////////////////////////
//
// Parse the returned AT^SIND=eons,2 data (callback function)
//
//////////////////////////////////////////////////////////////////////////////
HRESULT CEonsHandling::ParseEONSData(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam) {
CEonsHandling*  pInstance = (CEonsHandling*)pParam;
UINT            nType, nMode;
WCHAR           wszEonsOperator[EONS_MAX_OPN_LEN + 1];
WCHAR           wszServiceProvider[EONS_MAX_OPN_LEN + 1];
HRESULT         Ret = E_FAIL;

  pBlob = NULL;
  cbBlob = 0;
  Ret = E_FAIL;

  if (!pInstance) {
    return E_FAIL;
  }

  memset(pInstance->m_szModuleLongOperatorName, 0, sizeof(pInstance->m_szModuleLongOperatorName));
  memset(pInstance->m_szModuleShortOperatorName, 0, sizeof(pInstance->m_szModuleShortOperatorName));
  memset(wszEonsOperator, 0, sizeof(wszEonsOperator));
  memset(wszServiceProvider, 0, sizeof(wszServiceProvider));

  // Parse "^SIND: eons, <Mode>,<Type>,<"Eons-Operator-Name">,<"Service-Provider-Name">"
  // "Service-Provider-Name" is delivered by the module but currently not needed for eons support
  if (ParseRspPrefix(szRsp, szRsp)                        &&
      MatchStringBeginning(szRsp, "^SIND: eons,", szRsp)  &&
      ParseUInt(szRsp, TRUE, nMode, szRsp)                &&
      ParseRspParamSeparator(szRsp, szRsp)                &&
      ParseUInt(szRsp, TRUE, nType, szRsp)                &&
      ParseRspParamSeparator(szRsp, szRsp)                &&
      ParseQuotedEncodedString(ENCODING_UCS2, szRsp, wszEonsOperator, wszEonsOperator + (EONS_MAX_OPN_LEN + 1)) &&
      ParseRspParamSeparator(szRsp, szRsp)                &&
      ParseQuotedEncodedString(ENCODING_UCS2, szRsp, wszServiceProvider, wszServiceProvider + (EONS_MAX_OPN_LEN + 1)) &&
      MatchStringAnywhere(szRsp, "\r\n", szRsp)) {  // Skip optional service provider type and rest of response

    // Store EONS type and service provider name for later use.
    pInstance->m_nEONSType = nType;
    (void)_snprintfz(pInstance->m_szModuleServiceProviderName, EONS_MAX_OPN_LEN, "%S", wszServiceProvider);

    // Convert the found unicode string
    if ((pInstance->UseServiceProvider() == USE_SERVICE_PROVDER_NAME_ON || pInstance->UseServiceProvider() == USE_SERVICE_PROVDER_NAME_ON_NAMEONLY) && wcslen(wszServiceProvider) > 0) {
      // Use service provider name instead of operator name
      (void)_snprintfz(pInstance->m_szModuleLongOperatorName, EONS_MAX_OPN_LEN, "%S", wszServiceProvider);
      (void)_snprintfz(pInstance->m_szModuleShortOperatorName, EONS_MAX_OPN_LEN, "%S", wszServiceProvider);
      Ret = S_OK;
    }
    else if (pInstance->UseServiceProvider() == USE_SERVICE_PROVDER_NAME_COMBINATION && wcslen(wszServiceProvider) > 0) {
      // Use both service provider name and operator name.
      // The operator name will be according of the modules name table, regardless
      // of the setting of USE_MODULE_TABLE_OF_OPERATORS.
      (void)_snprintfz(pInstance->m_szModuleLongOperatorName, EONS_MAX_OPN_LEN, "%S %S", wszEonsOperator, wszServiceProvider);
      (void)_snprintfz(pInstance->m_szModuleShortOperatorName, EONS_MAX_OPN_LEN, "%S %S", wszEonsOperator, wszServiceProvider);
      Ret = S_OK;
    }
    else {
#ifdef USE_MODULE_TABLE_OF_OPERATORS
      if ((nType == 1) || (nType == 4)) {  // Type 4: name according to built in table of the module
#else
      if ((nType == 1)) { // We don't use Type 4 entries (name according to module's built in list), since the RIL has got a newer version of the list of operators
#endif
        (void)_snprintfz(pInstance->m_szModuleLongOperatorName, EONS_MAX_OPN_LEN, "%S", wszEonsOperator);
        Ret = S_OK;
      } else if ((nType == 2) || (nType == 3)) {
        if (pInstance->m_fIsLongName == TRUE) {
          (void)_snprintfz(pInstance->m_szModuleLongOperatorName, EONS_MAX_OPN_LEN, "%S", wszEonsOperator);
        } else {
          (void)_snprintfz(pInstance->m_szModuleShortOperatorName, EONS_MAX_OPN_LEN, "%S", wszEonsOperator);
        }
        Ret = S_OK;
      }
    }
  }

  return Ret;
}

#ifdef SUPPORTS_EHSX_R1
//////////////////////////////////////////////////////////////////////////////
//
// Parse the returned AT+XCOPS data
//
//////////////////////////////////////////////////////////////////////////////
HRESULT CEonsHandling::ParseXCOPSData(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam) {
CEonsHandling*  pInstance = (CEonsHandling*)pParam;
UINT            nType, nEONSType;
WCHAR           wszEonsOperator[EONS_MAX_OPN_LEN + 1];
HRESULT         Ret = E_FAIL;

  pBlob = NULL;
  cbBlob = 0;
  Ret = E_FAIL;

  if (!pInstance) {
    return E_FAIL;
  }

  memset(pInstance->m_szModuleLongOperatorName, 0, sizeof(pInstance->m_szModuleLongOperatorName));
  memset(pInstance->m_szModuleShortOperatorName, 0, sizeof(pInstance->m_szModuleShortOperatorName));

  // Parse "^SIND: eons, <Mode>,<Type>,<"Eons-Operator-Name">,<"Service-Provider-Name">"
  // "Service-Provider-Name" is delivered by the module but currently not needed for eons support
  if (ParseRspPrefix(szRsp, szRsp)                        &&
      MatchStringBeginning(szRsp, "+XCOPS:", szRsp)  &&
      ParseUInt(szRsp, TRUE, nType, szRsp)                &&
      ParseRspParamSeparator(szRsp, szRsp)                &&
      ParseQuotedEncodedString(ENCODING_UCS2, szRsp, wszEonsOperator, wszEonsOperator + (EONS_MAX_OPN_LEN + 1)) &&
      MatchStringAnywhere(szRsp, "\r\n", szRsp)           &&       // Skip optional service provider type and rest of response
      ParseRspPostfix(szRsp, szRsp)) {

    // Store EONS type and service provider name for later use.
    switch (nType) {
        case 1:
        case 2:
            nEONSType = 4;
            break;
        case 8:
        case 9:
            nEONSType = 1;
            break;
        case 3:
        case 4:
            nEONSType = 2;
            break;
        case 5:
        case 6:
            nEONSType = 3;
            break;
        case 0:
            nEONSType = 5;
            break;
        default:
            nEONSType = 5;
    }
    pInstance->m_nEONSType = nEONSType;

    (void)_snprintfz(pInstance->m_szModuleLongOperatorName, EONS_MAX_OPN_LEN, "%S", wszEonsOperator);
    Ret = S_OK;
  }

  return Ret;
}
#endif

//////////////////////////////////////////////////////////////////////////////
//
// Parse the returned SIM data (callback function)
//
//////////////////////////////////////////////////////////////////////////////
HRESULT CEonsHandling::ParseSimData(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam) {
CEonsHandling*  pInstance = (CEonsHandling*)pParam;
UINT            i;
UINT            nSW1;
UINT            nSW2;
LPCSTR          szResponseString;
UINT            cbSize;
BYTE*           pbResponseWalk;
LPCSTR          pchResponseStringWalk;
HRESULT         Ret;
BOOL            fIgnoreNextLine;

  pBlob = NULL;
  cbBlob = 0;

  if (!pInstance) {
    return E_FAIL;
  }

  pInstance->m_dwLines = 0;
  fIgnoreNextLine = pInstance->MustReadIMSIbeforeReadingSIM();

  Ret = S_OK;
  for(;;) {
    // Parse "<prefix>+CRSM: <sw1>,<sw2>"
    if (!ParseRspPrefix(szRsp, szRsp)                   ||
        !MatchStringBeginning(szRsp, "+CRSM: ", szRsp)  ||
        !ParseUInt(szRsp, TRUE, nSW1, szRsp)            ||
        !ParseRspParamSeparator(szRsp, szRsp)           ||
        !ParseUInt(szRsp, TRUE, nSW2, szRsp)) {
      break;
    }

    // Store the SIM response
    pInstance->m_wSimSw1 = (WORD)nSW1;
    pInstance->m_wSimSw2 = (WORD)nSW2;

    // Check the return values
    if ((nSW1 != 144) || (nSW2 != 0)) {
      if (!FindRspPostfix(szRsp, szRsp)) {
        Ret = E_FAIL;
      }
      break;
    }

    // Parse ","
    if (!ParseRspParamSeparator(szRsp, szRsp)) {
      // No response data present
      if (!FindRspPostfix(szRsp, szRsp)) {
        Ret = E_FAIL;
      }
      break;
    }

    if (!MatchStringBeginning(szRsp, "\"", szRsp)) {
      Ret = E_FAIL;
      break;
    }

    // Parse "<postfix>" and remember start of SIM data
    szResponseString = szRsp;
    if (!MatchStringAnywhere(szRsp, "\"", szRsp)) {
      Ret = E_FAIL;
      break;
    }

    // Calculate size of SIM data
    cbSize = ((szRsp - szResponseString)) / 2;

    // Check the data size
    if (cbSize == 0) {
      Ret = E_FAIL;
      break;
    }
    // Limit to maximum buffer size (the HC25 sometimes returns more bytes, that requested or actually exist).
    if (cbSize > EONS_MAX_BUF_BYTES) {
        cbSize = EONS_MAX_BUF_BYTES;
    }

    // Decode the response data
    pbResponseWalk = pInstance->m_bBuffer[pInstance->m_dwLines];
    pchResponseStringWalk = szResponseString;
    for (i = 0; i < cbSize; i++) {
      *pbResponseWalk = SemiByteCharsToByte(*pchResponseStringWalk, *(pchResponseStringWalk + 1));
      pbResponseWalk++;
      pchResponseStringWalk += 2;
    }

    if (!fIgnoreNextLine) {
      pInstance->m_dwBytes[pInstance->m_dwLines] = cbSize;
      pInstance->m_dwLines++;
    }
    fIgnoreNextLine = FALSE;

    if (pInstance->m_dwLines >= EONS_MAX_BUF_LINES) {
      Ret = E_FAIL;
      break;
    }
  }

  return Ret;
}

//////////////////////////////////////////////////////////////////////////////
//
// Parse the returned SMONI data (callback function)
//
//////////////////////////////////////////////////////////////////////////////
HRESULT CEonsHandling::ParseSmoniData(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam) {
    CEonsHandling*  pInstance = (CEonsHandling*)pParam;
    HRESULT hr = S_OK;
    UINT nMCC = 0;
    UINT nMNC = 0;
    UINT nLAC = 0;
    UINT nValue;

    pBlob = NULL;
    cbBlob = 0;

    if (!pInstance) {
        hr = E_FAIL;
        goto Error;
    }

    pInstance->m_wCurrentMCC = 0;
    pInstance->m_wCurrentMNC = 0;
    pInstance->m_wCurrentLAC = 0;

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

    // Different parameter sets for 2G and 3G networks.
    if (MatchStringBeginning(szRsp, "2G", szRsp)) {     // 2G network
        // Parse "<afrcn>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        (void)ParseUInt(szRsp, TRUE, nValue, szRsp);
        // Parse "<bcch>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        (void)ParseIntAsUInt(szRsp, nValue, szRsp, FALSE);
        // Parse "<mcc>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        if (ParseUInt(szRsp, TRUE, nValue, szRsp)) {
            nMCC = nValue;
        }
        // Parse "<mnc>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        if (ParseUInt(szRsp, TRUE, nValue, szRsp)) {
            nMNC = nValue;
        }
        // Parse "<lac>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        if (ParseHexUInt(szRsp, TRUE, nValue, szRsp)) {
            nLAC = nValue;
        }
        // Parse "<cell>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        (void)ParseHexUInt(szRsp, TRUE, nValue, szRsp);
        // Parse "<c1>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        (void)ParseUInt(szRsp, TRUE, nValue, szRsp);
        if (!FindRspPostfix(szRsp, szRsp)) {       // Skip over the rest
            hr = E_FAIL;
            goto Error;
        }
    }
    else if (MatchStringBeginning(szRsp, "3G", szRsp)) {        // 3G network
        // Parse "<uarfcn>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        (void)ParseUInt(szRsp, TRUE, nValue, szRsp);
        // Parse "<psc>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        (void)ParseUInt(szRsp, TRUE, nValue, szRsp);
        // Parse "<ec>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        (void)ParseUInt(szRsp, TRUE, nValue, szRsp);
        // Parse "<rscp>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        (void)ParseIntAsUInt(szRsp, nValue, szRsp, FALSE);
        // Parse "<mcc>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        if (ParseUInt(szRsp, TRUE, nValue, szRsp)) {
            nMCC = nValue;
        }
        // Parse "<mnc>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        if (ParseUInt(szRsp, TRUE, nValue, szRsp)) {
            nMNC = nValue;
        }
        // Parse "<lac>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        if (ParseHexUInt(szRsp, TRUE, nValue, szRsp)) {
            nLAC = nValue;
        }
        // Parse "<cell>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        (void)ParseHexUInt(szRsp, TRUE, nValue, szRsp);
        // Parse "<c1>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        (void)ParseUInt(szRsp, TRUE, nValue, szRsp);
        if (!FindRspPostfix(szRsp, szRsp)) {       // Skip over the rest
            hr = E_FAIL;
            goto Error;
        }
    }
    else {
        hr = E_FAIL;
        goto Error;
    }

    pInstance->m_wCurrentMCC = (WORD)nMCC;
    pInstance->m_wCurrentMNC = (WORD)nMNC;
    pInstance->m_wCurrentLAC = (WORD)nLAC;

Error:
    return hr;
}

//////////////////////////////////////////////////////////////////////////////
//
// Thread, which read
//
//////////////////////////////////////////////////////////////////////////////
DWORD CEonsHandling::EONSInitThreadProc() {
    BOOL bEONSenabled;
    UINT iTable;
    DWORD dwDisableEONSSIMReadTable;

    if (!GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("DisableEONSSIMReadTable"), &dwDisableEONSSIMReadTable))
        dwDisableEONSSIMReadTable = 0;

    m_fEONSTableAvailable = FALSE;
    m_fDisableEONSSIMReadTable = (dwDisableEONSSIMReadTable == 1 ? TRUE : FALSE);

    // First try to enable module internal EONS handling
    bEONSenabled = EnableModuleHandling();
    if (bEONSenabled)
    {
        // Module supports EONS
        m_fIsLongName = IsLongName();
        m_SupportType = EONS_ENABLED;
    }
    // If the module doesn't support EONS or has the bug of not
    // reading the SIM files currectly, read the SIM files now.
    if (!bEONSenabled || (MUST_CHECK_SIM_FOR_EONS && !m_fDisableEONSSIMReadTable))
    {
        SYNCBLOCK(m_csRunningReadTable);

        // Module doesn't support EONS -> try to emulate it
        // Load the EONS tables...
        iTable = LoadPnnTable();
        if (iTable == READ_EONS_SIM_OK)
        {
            iTable = LoadOplTable();
        }

        if (iTable == READ_EONS_SIM_OK)
        {
            // Everthing is perfectly fine
            m_fEONSTableAvailable = TRUE;
            if (!bEONSenabled)
                m_SupportType = EONS_EMULATED;
        }
        else if (iTable != READ_EONS_SIM_OK)
        {
            // SIM doesn't support EONS
            if (!bEONSenabled)
                m_SupportType = EONS_NOT_AVAILABLE;
        }
    }

#ifdef RIL_NOTIFY_EONS
    // Let MMI know that EONS is available
    if ((m_SupportType == EONS_ENABLED) || (m_SupportType == EONS_EMULATED)) {
        (void)m_pRilDevice->BroadcastRealBlobNotification(RIL_NOTIFY_EONS, NULL, 0);
    }
#endif // RIL_NOTIFY_EONS

    // Send some useful EONS info to debug output if enabled
    TraceEonsInfo();

    //We have to unlock the blocked function RILDrv_GetCurrentOperator() after EONS initialization in any case !!!
    (void)SetEvent(m_hReadyEvent);

    return 0;
}

//////////////////////////////////////////////////////////////////////////////
//
// Trace content of SIM Service Table
//
//////////////////////////////////////////////////////////////////////////////
void CEonsHandling::ReadSIMServiceTable(void) {
    DWORD dwReadServiceTabe = 1;
    char szCmd[MAX_ATCMD_LEN];
    char szPath[MAX_PATH];
    INT iFileLen = 0;

    (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("ReadSIMServiceTable"), &dwReadServiceTabe);
    if (dwReadServiceTabe != 1)
        return;

    if (IsForbiddenSIMFile(EF_SMST))
        return;

    if (m_fMustReadIMSIbeforeReadingSIM)
        (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CRSM=192,28423,0,0,15;+CRSM=192,%d,0,0,%d", EF_SMST, m_pRilDevice->GetCurrentSIMType() != SIMTYPE_2G_SIM ? 63 : 15);
    else
        (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CRSM=192,%d,0,0,%d", EF_SMST, m_pRilDevice->GetCurrentSIMType() != SIMTYPE_2G_SIM ? 63 : 15);
    if (NeedSIMPath(m_pRilDevice->GetCurrentSIMType(), EF_SMST, szPath, MAX_PATH)) {
        (void)strncat(szCmd, ",,\"", MAX_ATCMD_LEN - strlen(szCmd));
        (void)strncat(szCmd, szPath, MAX_ATCMD_LEN - strlen(szCmd));
        (void)strncat(szCmd, "\"", MAX_ATCMD_LEN - strlen(szCmd));
    }
    (void)strncat(szCmd, "\r", MAX_ATCMD_LEN - strlen(szCmd));
    if (!QueueInternalCmdBlocking(HCCMD_STANDARD, m_pRilDevice, szCmd, CMDOPT_RETRYONINVALIDINDEX, APIID_SENDRESTRICTEDSIMCMD, 0, 0, 0, ParseSimData, NULL, this))
        return;
    if (!GetSIMFIleInfo(m_pRilDevice->GetCurrentSIMType(), iFileLen))
      return;
    if (m_fMustReadIMSIbeforeReadingSIM)
        (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CRSM=192,28423,0,0,15;+CRSM=176,%d,0,0,%d", EF_SMST, iFileLen);
    else
        (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CRSM=176,%d,0,0,%d", EF_SMST, iFileLen);
    if (NeedSIMPath(m_pRilDevice->GetCurrentSIMType(), EF_SMST, szPath, MAX_PATH)) {
        (void)strncat(szCmd, ",,\"", MAX_ATCMD_LEN - strlen(szCmd));
        (void)strncat(szCmd, szPath, MAX_ATCMD_LEN - strlen(szCmd));
        (void)strncat(szCmd, "\"", MAX_ATCMD_LEN - strlen(szCmd));
    }
    (void)strncat(szCmd, "\r", MAX_ATCMD_LEN - strlen(szCmd));
    (void)QueueInternalCmd(HCCMD_STANDARD, m_pRilDevice, szCmd, CMDOPT_RETRYONINVALIDINDEX, APIID_SENDRESTRICTEDSIMCMD, 0, 0, 0, ParseSimData, NULL, this);
}

