view src/aci2/aci/cmh_mmf.c @ 638:cab2f315827e

FFS dev.c: added Spansion PL032J to the "generic" table With the discovery of first GTM900 and then Tango, it now appears that Openmoko was not the only manuf after all who kept TI's TCS211 firmware largely intact (as opposed to changing it beyond all recognition like Compal, Chi-Mei and BenQ did), thus we are now getting new "alien" targets on which we reuse the original manuf's FFS with IMEI and RF calibration tables as if it were native. On these targets we use the original device table for FFS, even though we previously thought that it would never apply to any target other than dsample, leonardo and gtamodem. We have previously added Samsung K5L33xxCAM (a new kind of multi-ID device) to the generic table to support its use in Huawei GTM900-B modules; now we got news that some slightly older GTM900-B specimen used S71PL032J instead, so we are now adding PL032J as well.
author Mychaela Falconia <falcon@freecalypso.org>
date Thu, 30 Jan 2020 17:45:48 +0000
parents 93999a60b835
children
line wrap: on
line source

/*
+-----------------------------------------------------------------------------
|  Project :  GSM-PS (6147)
|  Modul   :  CMH_MMF
+-----------------------------------------------------------------------------
|  Copyright 2002 Texas Instruments Berlin, AG
|                 All rights reserved.
|
|                 This file is confidential and a trade secret of Texas
|                 Instruments Berlin, AG
|                 The receipt of or possession of this file does not convey
|                 any rights to reproduce or disclose its contents or to
|                 manufacture, use, or sell anything it may describe, in
|                 whole, or in part, without the specific written consent of
|                 Texas Instruments Berlin, AG.
+-----------------------------------------------------------------------------
|  Purpose :  This module defines the functions used by the command
|             handler for mobility management.
+-----------------------------------------------------------------------------
*/

#ifndef CMH_MMF_C
#define CMH_MMF_C
#endif

#include "aci_all.h"

/*==== INCLUDES ===================================================*/
#include "aci_cmh.h"
#include "ati_cmd.h"
#include "aci_cmd.h"
#include "aci_mem.h"
#include "pcm.h"

#ifdef FAX_AND_DATA
#include "aci_fd.h"
#endif    /* of #ifdef FAX_AND_DATA */

#include "aci.h"
#include "psa.h"
#include "psa_cc.h"
#include "psa_mm.h"
#include "psa_sim.h"
#include "psa_sat.h"
#include "psa_util.h"
#include "cmh.h"

#ifdef DTI
#include "dti.h"      /* functionality of the dti library */
#include "dti_conn_mng.h"
/* #include "dti_cntrl_mng.h" */
#endif


#ifdef GPRS
#include "gaci.h"
#include "gaci_cmh.h"
#include "psa_gmm.h"
#include "cmh_gmm.h"
#endif /* GPRS */

#include "cmh_mm.h"
#include "cmh_sim.h"


#ifndef _SIMULATION_
#include "ffs/ffs.h"
#include "ffs_coat.h"
#endif /* !_SIMULATION_ */

#ifdef FF_CPHS
#include "cphs.h"
#endif /* FF_CPHS */

/*==== CONSTANTS ==================================================*/
/* Countries, for which the ECC should be ignored, i.e. done as normal call */
/* contains MCC and ECC, last element should be always NULL*/
GLOBAL const T_ECCIgnoreRec ECCIgnoreTable[] =
{
  { 0x260, ALL_MNC, "999" },   /* Poland         */
  { 0x454, 0x04F,   "112" },   /* "HK ORANGE"    */
  { 0x454, 0x10F,   "112" },   /* "HK New World" */
  {    -1,    -1,   NULL  }    /* End mark       */
};

#define ONS_EXT_DCS 0x80

/*==== TYPES ======================================================*/

/*==== EXPORT =====================================================*/

/*==== VARIABLES ==================================================*/
LOCAL T_OPER_ENTRY ONSDesc;
GLOBAL T_ONS_READ_STATE ONSReadStatus = ONS_READ_NOT_DONE;

/* This is required because FFS is not available in the Simulation Mode */
#ifdef _SIMULATION_
  T_FFSPLMNIMSI RAMPlmnIMSI = {0};
#endif

#if defined GPRS AND defined (DTI)
EXTERN T_GMM_SHRD_PRM gmmShrdPrm;
#endif
/*==== FUNCTIONS ==================================================*/

/*
+-------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)         MODULE  : CMH_MM                  |
|                                 ROUTINE : cmhMM_wild_compare      |
+-------------------------------------------------------------------+

  PURPOSE : 

*/

LOCAL BOOL cmhMM_wild_compare( int sim, int bcch )
{
  if ((bcch & 0x00F) != (sim & 0x00F) AND (sim & 0x00F) != 0x00D)
    return FALSE;
  if ((bcch & 0x0F0) != (sim & 0x0F0) AND (sim & 0x0F0) != 0x0D0)
    return FALSE;
  if ((bcch & 0xF00) != (sim & 0xF00) AND (sim & 0xF00) != 0xD00)
    return FALSE;

  return TRUE;
}


/*
+-------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)         MODULE  : CMH_MM                  |
|                                 ROUTINE : cmhSIM_plmn_equal_sim_wc|
+-------------------------------------------------------------------+

  PURPOSE : 

*/

LOCAL BOOL cmhSIM_plmn_equal_sim_wc (int bcch_mcc, int bcch_mnc, 
                                     int  sim_mcc, int  sim_mnc)
{
  /*TRACE_FUNCTION ("cmhSIM_plmn_equal_sim()");*/

  /* Check MCC */
  if (!cmhMM_wild_compare(sim_mcc, bcch_mcc))
    return FALSE;

  /* Check first 2 MNC digits */
  if (!cmhMM_wild_compare(sim_mnc & 0xff0, bcch_mnc & 0xff0))
    return FALSE;

  /* Check for full match */
  if (cmhMM_wild_compare(sim_mnc & 0xf, bcch_mnc & 0xf))
    return TRUE;

  /* The 3rd digit of the MNC differs */
  if ((bcch_mcc >= 0x310) AND (bcch_mcc <= 0x316))
  {
    /* 
     * The MCC is in the range 310..316, this means North America.
     * The zero suffix rule applies.
     */
    return ((((sim_mnc & 0xf) EQ 0xf) AND ((bcch_mnc & 0xf) EQ 0x0)) OR
            (((sim_mnc & 0xf) EQ 0x0) AND ((bcch_mnc & 0xf) EQ 0xf)));
  }
  return ((bcch_mnc & 0xf) EQ 0xf);
}


/*
+-------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)         MODULE  : CMH_MM                  |
|                                 ROUTINE : cmhMM_FindPLMN          |
+-------------------------------------------------------------------+

  PURPOSE : scan the list of operators to find the PLMN
            representation.

  NOTE:     This function looks as if it could be simplified and
            shortened. However, it is expected that it works.

*/

GLOBAL BOOL cmhMM_FindPLMN( T_OPER_ENTRY * plmnDesc,
                            SHORT mcc, SHORT mnc, U16 lac, BOOL nw_search )
{
  int     i;                          /* holds list idx */
  EF_PLMN plmn;                       /* holds PLMN identifier */
  SHORT   sim_mcc;                    /* Integer representation MCC */
  SHORT   sim_mnc;                    /* Integer representation MNC */
  USHORT  maxRec;                     /* holds maximum records */
  USHORT  recNr;                      /* holds record number */
  UBYTE   retVal;                     /* holds return value */
  UBYTE   ver;                        /* holds version */

/*
 *-------------------------------------------------------------------
 * search list of PNN records if SIM is EONS enabled
 *-------------------------------------------------------------------
 */

  TRACE_FUNCTION ("cmhMM_FindPLMN()");
  TRACE_EVENT_P3 ("mcc: %x, mnc %x, lac: %x", mcc, mnc, lac);

  /* Initial conditions for Data Coding Scheme and PLMN name source */
  memset (plmnDesc, 0, sizeof (T_OPER_ENTRY));
/*  plmnDesc.dcs = 0; */
  plmnDesc->pnn = FALSE;

  if(psaSIM_ChkSIMSrvSup(SRV_PNN))
  {
    if (!nw_search) /* this is not for network search list */
    {
      if(psaSIM_ChkSIMSrvSup(SRV_OPL) || cmhSIM_plmn_is_hplmn (mcc, mnc))
      {
        if (mmShrdPrm.PNNLst.plmn.v_plmn EQ VLD_PLMN) 
        {
          plmnDesc->mcc = mcc;
          plmnDesc->mnc = mnc;
          plmnDesc->pnn = TRUE;
          plmnDesc->long_len     = mmShrdPrm.PNNLst.long_len;
          plmnDesc->long_ext_dcs = mmShrdPrm.PNNLst.long_ext_dcs;
          plmnDesc->shrt_len     = mmShrdPrm.PNNLst.shrt_len;
          plmnDesc->shrt_ext_dcs = mmShrdPrm.PNNLst.shrt_ext_dcs;

          if(mmShrdPrm.PNNLst.long_len)
          {
            memcpy (plmnDesc->longName,
                    mmShrdPrm.PNNLst.long_name,
                    MINIMUM(mmShrdPrm.PNNLst.long_len, MAX_LONG_OPER_LEN - 1));
          }

          if(mmShrdPrm.PNNLst.shrt_len > 0)
          {
            memcpy (plmnDesc->shrtName,
                    mmShrdPrm.PNNLst.shrt_name,
                    MINIMUM(mmShrdPrm.PNNLst.shrt_len, MAX_SHRT_OPER_LEN - 1));
          }
          return TRUE;
        }
        else
        {
          U16 current_lac = lac;
          if (current_lac EQ NOT_PRESENT_16BIT)
            current_lac = mmShrdPrm.lac;

          for (i=0; i<simShrdPrm.opl_list.num_rcd; i++)
          {
            int pnn_rec_number = simShrdPrm.opl_list.opl_rcd[i].pnn_rec_num;
            SHORT sim_mcc, sim_mnc;

            cmhSIM_getMncMccFrmPLMNsel (simShrdPrm.opl_list.opl_rcd[i].plmn,
                                        &sim_mcc, &sim_mnc);

            if (pnn_rec_number EQ 0xFF)
              continue; /* empty record, continue with next one */

            if(cmhSIM_plmn_equal_sim_wc( mcc, mnc, sim_mcc, sim_mnc) AND
               ( (simShrdPrm.opl_list.opl_rcd[i].lac1 EQ 0x0000 AND
                  simShrdPrm.opl_list.opl_rcd[i].lac2 EQ 0xFFFE)
               OR
                 (simShrdPrm.opl_list.opl_rcd[i].lac1 <= current_lac AND
                  simShrdPrm.opl_list.opl_rcd[i].lac2 >= current_lac) ) )
            {
              if (pnn_rec_number EQ 0)
                break; /* 31.102: 4.2.59	EFOPL (Operator PLMN List):
                       A value of '00' indicates that the name is to be taken from other sources, see TS 22.101 [24] */

              pnn_rec_number--; /* adjust to zero based array */
              
              if (simShrdPrm.pnn_list.pnn_rcd[pnn_rec_number].v_plmn)
              {
                plmnDesc->mcc = mcc;
                plmnDesc->mnc = mnc;
                plmnDesc->pnn = TRUE;
                plmnDesc->long_len = 
                  MINIMUM (simShrdPrm.pnn_list.pnn_rcd[pnn_rec_number].long_len, MAX_LONG_OPER_LEN - 1);
                memcpy (plmnDesc->longName,
                        simShrdPrm.pnn_list.pnn_rcd[pnn_rec_number].long_name,
                        plmnDesc->long_len);
                plmnDesc->long_ext_dcs =     simShrdPrm.pnn_list.pnn_rcd[pnn_rec_number].long_ext_dcs;
                plmnDesc->shrt_len =
                  MINIMUM (simShrdPrm.pnn_list.pnn_rcd[pnn_rec_number].shrt_len, MAX_SHRT_OPER_LEN - 1);
                memcpy (plmnDesc->shrtName, 
                        simShrdPrm.pnn_list.pnn_rcd[pnn_rec_number].shrt_name,
                        plmnDesc->shrt_len); 
                plmnDesc->shrt_ext_dcs =     simShrdPrm.pnn_list.pnn_rcd[pnn_rec_number].shrt_ext_dcs;
                return TRUE;
              }
            }
          }
        }
      }
    }

    else /* the procedure is used for building the PLMN selection list */
    {
      T_pnn_name *current;  /* points to PNN element currently compared */
      current = (T_pnn_name *)mmShrdPrm.PNNLst.next;
      while (current != NULL)
      {
        SHORT sim_mcc, sim_mnc;
        cmhMM_CnvrtPLMN2INT (current->plmn.mcc, current->plmn.mnc,
                             &sim_mcc, &sim_mnc);
        if (cmhSIM_plmn_equal_sim (mcc, mnc, sim_mcc, sim_mnc) AND
            (current->lac EQ lac))
        {
          if(current->plmn.v_plmn EQ VLD_PLMN)
          {
            plmnDesc->mcc = mcc;
            plmnDesc->mnc = mnc;
            plmnDesc->pnn = TRUE;
            plmnDesc->long_len     = 
              MINIMUM (current->long_len, MAX_LONG_OPER_LEN - 1);
            plmnDesc->long_ext_dcs = current->long_ext_dcs;
            plmnDesc->shrt_len     = 
              MINIMUM (current->shrt_len, MAX_SHRT_OPER_LEN - 1);
            plmnDesc->shrt_ext_dcs = current->shrt_ext_dcs;
            if (current->long_len)
            {
              memcpy (plmnDesc->longName, 
                      current->long_name,
                      plmnDesc->long_len);
            }

            if (current->shrt_len)
            {
              memcpy (plmnDesc->shrtName,
                      current->shrt_name,
                      plmnDesc->shrt_len);
            }
            return TRUE;
          }
        }
        else
          current = (T_pnn_name *)current->next;
      }
    }
  }

  if((ONSReadStatus EQ ONS_READ_OVER) AND cmhSIM_plmn_is_hplmn (mcc, mnc))
  {

    *plmnDesc = ONSDesc; /* Struct assignment */

    plmnDesc->mcc = mcc;
    plmnDesc->mnc = mnc;
    return TRUE;
  }

/*
 *-------------------------------------------------------------------
 * search list of operators ( PCM table )
 *-------------------------------------------------------------------
 */
  recNr = 1;

  do
  {
    retVal= pcm_ReadRecord( (UBYTE *) EF_PLMN_ID, recNr, SIZE_EF_PLMN,
                            (UBYTE *)&plmn, &ver, &maxRec );

    if( retVal EQ PCM_INVALID_SIZE OR retVal EQ PCM_INVALID_RECORD )
      break;

    sim_mcc = (plmn.mcc[0] << 8) + plmn.mcc[1];
    sim_mnc = (plmn.mnc[0] << 8) + plmn.mnc[1];

    if (cmhSIM_plmn_equal_sim (mcc, mnc, sim_mcc, sim_mnc))
    {
      cmhMM_CnvrtTrmPCMOpNam( plmnDesc, &plmn );

      plmnDesc->mcc = sim_mcc;
      plmnDesc->mnc = sim_mnc;
      return TRUE;
    }

    recNr++;
  }
  while( recNr <= maxRec );

/*
 *-------------------------------------------------------------------
 * search list of operators ( fixed table )
 *-------------------------------------------------------------------
 */
  for( i = 0;
       operListFixed[i].mcc NEQ -1 AND operListFixed[i].mnc NEQ -1;
       i++ )
  {
    sim_mcc = operListFixed[i].mcc;
    sim_mnc = operListFixed[i].mnc;

    if (cmhSIM_plmn_equal_sim (mcc, mnc, sim_mcc, sim_mnc))
    {
      strncpy (plmnDesc->longName, 
               operListFixed[i].longName, 
               MAX_LONG_OPER_LEN - 1);
      strncpy (plmnDesc->shrtName,
               operListFixed[i].shrtName,
               MAX_SHRT_OPER_LEN - 1);
      plmnDesc->mcc      = operListFixed[i].mcc;
      plmnDesc->mnc      = operListFixed[i].mnc;
      return TRUE;
    }
  }

/*
 *-------------------------------------------------------------------
 * only mnc and mcc description available
 *-------------------------------------------------------------------
 */
  plmnDesc->mcc = mcc;
  plmnDesc->mnc = mnc;
  /*
   * The competitor has here a more sophisticated algorithm:
   * If they know the country code, they don't display the
   * MCC in numerical representation, but an abbreviation
   * for the country. We are satisfied displaying the MCC
   * and saving the ROM space for the country table.
   */
  if ( ( mnc & 0xF) NEQ 0xf ) /* is 3rd digit mnc? */
  {
    sprintf (plmnDesc->longName, "%03x %03x", mcc, mnc);
  }
  else
  { /* only 2-digit-MNC */
    mnc = (mnc & 0x0FF0) >> 4;
    sprintf (plmnDesc->longName, "%03x %02x", mcc, mnc);
  }
  strcpy (plmnDesc->shrtName, plmnDesc->longName);

  return TRUE;
}

/*
+-------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)         MODULE  : CMH_MM                  |
|                                 ROUTINE : cmhMM_isBandAllowed     |
+-------------------------------------------------------------------+

  PURPOSE : Check if band combination is part of bands allowed by manufacturer.
            To be used by AT%BAND.
            Per convention: if AllowedBands = 0, all bands are allowed.
                            band 0 is never allowed (that is not a combination !!)
*/

GLOBAL BOOL cmhMM_isBandAllowed( UBYTE band, UBYTE AllowedBands )
{
  if( AllowedBands EQ 0x00 )
  {
    /* no manufacturer constraint */
    return(TRUE);
  }
  if( band EQ 0x00)
  {
    return(FALSE);
  }

  if( (band & AllowedBands) EQ band )
  {
    return( TRUE );
  }
  else
    return( FALSE );
}

/*
+---------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)         MODULE  : CMH_MM                    |
|                                 ROUTINE : cmhMM_writeSetBand        |
+---------------------------------------------------------------------+

  PURPOSE :

*/
#define SIZE_OF_RFCAP (16*sizeof(UBYTE))

GLOBAL BOOL cmhMM_writeSetBand( UBYTE setband )
{
#ifndef _SIMULATION_
  T_FFS_RET ret_ffs;
  UBYTE     RFCap[SIZE_OF_RFCAP]; /* RFCap has 16 bytes */

  ret_ffs = FFS_fread("/gsm/com/rfcap",
                      RFCap,
                      SIZE_OF_RFCAP);
  if(ret_ffs < 1)  /* error */
  {
    TRACE_EVENT_P1("RFCap: cannot read FFS: error n: %d", ret_ffs);
    return(FALSE);
  }

  /* write user bands into FFS */
  RFCap[0] = setband;

  ret_ffs = FFS_fwrite("/gsm/com/rfcap",
                       RFCap,
                       SIZE_OF_RFCAP);

  if( ret_ffs NEQ EFFS_OK )
  {
    TRACE_EVENT_P1("Cannot write value on RFCap, error n: %d", ret_ffs);
    return(FALSE);
  }
  TRACE_EVENT("cmhMM_writeSetBand: data writing in FFS successful");
#endif /* _SIMULATION_ */

  return( TRUE );
}

/*
+---------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)         MODULE  : CMH_MM                    |
|                                 ROUTINE : cmhMM_getBandSettings     |
+---------------------------------------------------------------------+

  PURPOSE :
*/

GLOBAL void cmhMM_getBandSettings( UBYTE *SetBands, UBYTE *AllowedBands )
{
  UBYTE intern_set_bands     = 0x00, /* initialized to NOT SET */
        intern_allowed_bands = 0x00; /* avoid several checks against NULL */
#ifndef _SIMULATION_
  UBYTE RFCap[SIZE_OF_RFCAP]; /* RFCap has 16 bytes */
  T_FFS_RET ret_ffs;

  TRACE_FUNCTION("cmhMM_getBandSettings()");

  ret_ffs = FFS_fread("/gsm/com/rfcap",
                      RFCap,
                      SIZE_OF_RFCAP);
  if(ret_ffs < 1)  /* error */
  {
    TRACE_EVENT_P1("cmhMM_getBandSettings: RFCap: cannot read FFS: error n: %d", ret_ffs);
  }
  else
  {
    TRACE_EVENT("cmhMM_getBandSettings: data reading from FFS successful");
    intern_set_bands     = RFCap[0];
    intern_allowed_bands = RFCap[1];
  }
#endif /* #ifndef _SIMULATION_ */

  if( SetBands NEQ NULL )
  {
    *SetBands     = intern_set_bands;
    TRACE_EVENT_P1("User defined Band bitfield: %d", *SetBands);
  }
  if( AllowedBands NEQ NULL )
  {
    *AllowedBands = intern_allowed_bands;
    TRACE_EVENT_P1("Manufacturer defined Band bitfield: %d", *AllowedBands);
  }
}

/*
+-------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)         MODULE  : CMH_MM                  |
|                                 ROUTINE : cmhMM_FindNumeric       |
+-------------------------------------------------------------------+

  PURPOSE : Convert operator representation from string into numeric.

  NOTE:     The cmhMM_FindNumeric() function is not part of the public
            declared interface to ACI.
            However some guys from the MFW team are using this function 
            in mfw_nma.c, declaring the function prototype in the 
            nfw_nma.c file locally. Be careful with changes.

*/

GLOBAL BOOL cmhMM_FindNumeric( T_OPER_ENTRY * plmnDesc, const CHAR *numStr )
{
  USHORT idx;
  BOOL ready;

  TRACE_FUNCTION ("cmhMM_FindNumeric()");

  plmnDesc->mcc = 0;
  plmnDesc->mnc = 0;
  ready = FALSE;

  /*
   * Convert string representation into internal represention
   */
  for (idx = 0; idx < SIZE_MCC + SIZE_MNC; idx++)
  {
    if (idx < SIZE_MCC)
    {
      /*
       * Converting MCC
       */
      if (!ready AND
          numStr[idx] >= '0' AND numStr[idx] <= '9')
      {
        plmnDesc->mcc = (plmnDesc->mcc << 4) + numStr[idx] - '0';
      }
      else
      {
        ready = TRUE;
      }
    }
    else
    {
      /*
       * Converting MNC
       */
      if (!ready AND
          numStr[idx] >= '0' AND numStr[idx] <= '9')
      {
        plmnDesc->mnc = (plmnDesc->mnc << 4) + numStr[idx] - '0';
      }
      else
      {
        ready = TRUE;
        if ((plmnDesc->mnc & 0x00F) NEQ 0xF)
          plmnDesc->mnc = (plmnDesc->mnc << 4) + 0xF;
      }
    }
  }

/*
 *-------------------------------------------------------------------
 * search list of operators
 *-------------------------------------------------------------------
 */

  (void)cmhMM_FindPLMN (plmnDesc, plmnDesc->mcc, plmnDesc->mnc, NOT_PRESENT_16BIT, FALSE);

  /* decode 7 Bit default GSM for MFW */
  if (plmnDesc->pnn)
  {
    char longName[(MAX_ALPHA_OPER_LEN*8+6)/7];
    char shrtName[(MAX_ALPHA_OPER_LEN*8+6)/7];

    longName[0]=shrtName[0]='\0';

    if (plmnDesc->long_len)
    {
      switch (plmnDesc->long_ext_dcs>>4 & 0x07)
      {
        case 0x00:
          utl_cvtPnn7To8((UBYTE *)plmnDesc->longName,
                         (UBYTE)  plmnDesc->long_len,
                                  plmnDesc->long_ext_dcs,
                         (UBYTE *)longName);
          break;
        case 0x01:
          TRACE_ERROR ("ERROR: Unhandled UCS2");
          break;
        default:
          TRACE_ERROR ("ERROR: Unknown DCS");
          break;
      }
      strncpy (plmnDesc->longName, longName, MAX_LONG_OPER_LEN - 1);
      plmnDesc->longName[MAX_LONG_OPER_LEN - 1] = '\0';
      plmnDesc->long_len = strlen(longName);
    }
    if (plmnDesc->shrt_len)
    {
      switch (plmnDesc->shrt_ext_dcs>>4 & 0x07)
      {
        case 0x00:
          utl_cvtPnn7To8((UBYTE *)plmnDesc->shrtName,
                         (UBYTE)  plmnDesc->shrt_len,
                                  plmnDesc->shrt_ext_dcs,
                         (UBYTE *)shrtName);
          break;
        case 0x01:
          TRACE_ERROR ("ERROR: Unhandled UCS2");
          break;
        default:
          TRACE_ERROR ("ERROR: Unknown DCS");
          break;
      }
      strncpy (plmnDesc->shrtName, shrtName, MAX_SHRT_OPER_LEN - 1);
      plmnDesc->shrtName[MAX_SHRT_OPER_LEN - 1] = '\0';
      plmnDesc->shrt_len = strlen(shrtName);
    }
  }
  return TRUE;
}

/*
+-------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)         MODULE  : CMH_MM                  |
|                                 ROUTINE : cmhMM_FindName          |
+-------------------------------------------------------------------+

  PURPOSE : scan the list of operators to find the short or long name
            representation.
            
  NOTE:     The cmhMM_FindName() function is not part of the public
            declared interface to ACI.
            However some guys from the MFW team are using this function 
            in mfw_nma.c, declaring the function prototype in the 
            nfw_nma.c file locally. Be careful with changes.


*/

GLOBAL BOOL cmhMM_FindName( T_OPER_ENTRY * plmnDesc,
                      const CHAR *string, T_ACI_CPOL_FRMT format )
{
  USHORT  idx;                        /* holds list idx */
  EF_PLMN plmn;                       /* holds PLMN identifier */
  USHORT  maxRec;                     /* holds maximum records */
  USHORT  recNr;                      /* holds record number */
  UBYTE   retVal;                     /* holds return value */
  UBYTE   ver;                        /* holds version */
  char longName[(MAX_ALPHA_OPER_LEN*8+6)/7];
  char shrtName[(MAX_ALPHA_OPER_LEN*8+6)/7];

/*
 *-------------------------------------------------------------------
 * search list of PNN records if SIM is EONS enabled
 *-------------------------------------------------------------------
 */
  if(psaSIM_ChkSIMSrvSup(SRV_PNN) && psaSIM_ChkSIMSrvSup(SRV_OPL))
  {
    T_pnn_name *current;  /* points to PNN element currently compared */

    current = (T_pnn_name *)mmShrdPrm.PNNLst.next;
    while (current != NULL)
    {
      longName[0]=shrtName[0]='\0';

      if (current->long_len)
      {
        switch (current->long_ext_dcs>>4 & 0x07)
        {
          case 0x00:
            utl_cvtPnn7To8((UBYTE *)current->long_name,
                                    current->long_len,
                                    current->long_ext_dcs,
                           (UBYTE *)longName);
            break;
          case 0x01:
            TRACE_ERROR ("ERROR: Unhandled UCS2");
            break;
          default:
            TRACE_ERROR ("ERROR: Unknown DSC");
            break;
        }
      }

      if (current->shrt_len)
      {
        switch (current->shrt_ext_dcs>>4 & 0x07)
        {
          case 0x00:
            utl_cvtPnn7To8((UBYTE *)current->shrt_name,
                                    current->shrt_len,
                                    current->shrt_ext_dcs,
                           (UBYTE *)shrtName);
            break;
          case 0x01:
            TRACE_ERROR ("ERROR: Unhandled UCS2");
            break;
          default:
            TRACE_ERROR ("ERROR: Unknown DSC");
            break;
        }
      }

      if((format EQ CPOL_FRMT_Long) && (strcmp (string, (char *)longName) EQ 0) ||
         (format EQ CPOL_FRMT_Short) && (strcmp (string,(char *)shrtName) EQ 0))
      {
        plmnDesc->pnn = 1;
        /* decode Name since MFW expects 8-Bit string */
        strncpy (plmnDesc->longName, longName, MAX_LONG_OPER_LEN - 1);
        plmnDesc->longName[MAX_LONG_OPER_LEN - 1] = '\0';
        plmnDesc->long_len = strlen (plmnDesc->longName);

        strncpy (plmnDesc->shrtName, shrtName, MAX_SHRT_OPER_LEN - 1);
        plmnDesc->shrtName[MAX_SHRT_OPER_LEN -1] = '\0';
        plmnDesc->shrt_len = strlen (plmnDesc->shrtName);

        cmhMM_CnvrtPLMN2INT( current->plmn.mcc,
                             current->plmn.mnc,
                             &plmnDesc->mcc, &plmnDesc->mnc );

        if (current->plmn.v_plmn EQ VLD_PLMN)
        {
          return TRUE;
        }
      }
      else
        current = (T_pnn_name *)current->next;
    }
  }

/*
 *-------------------------------------------------------------------
 * search list of operators ( PCM table )
 *-------------------------------------------------------------------
 */
  recNr = 1;

  do
  {
    retVal= pcm_ReadRecord( (UBYTE *) EF_PLMN_ID, recNr, SIZE_EF_PLMN,
                            (UBYTE *)&plmn, &ver, &maxRec );

    if( retVal EQ PCM_INVALID_SIZE OR retVal EQ PCM_INVALID_RECORD )
      break;

    cmhMM_CnvrtTrmPCMOpNam( plmnDesc, &plmn );

    if ((format EQ CPOL_FRMT_Short AND
         strcmp(plmnDesc->shrtName, string) EQ 0) OR
        (format EQ CPOL_FRMT_Long AND
         strcmp(plmnDesc->longName, string) EQ 0))
    {
      plmnDesc->mcc = (plmn.mcc[0] << 8) + plmn.mcc[1];
      plmnDesc->mnc = (plmn.mnc[0] << 8) + plmn.mnc[1];
      return TRUE;
    }

    recNr++;
  }
  while( recNr <= maxRec );

/*
 *-------------------------------------------------------------------
 * search list of operators ( fixed table )
 *-------------------------------------------------------------------
 */
  for( idx = 0; operListFixed[idx].shrtName NEQ NULL; idx++ )
  {
    if ((format EQ CPOL_FRMT_Short AND
         strcmp((char*)operListFixed[idx].shrtName, string) EQ 0) OR
        (format EQ CPOL_FRMT_Long AND
         strcmp((char*)operListFixed[idx].longName, string) EQ 0))
    {
      memset (plmnDesc, 0, sizeof (T_OPER_ENTRY));
      strncpy (plmnDesc->longName, 
               operListFixed[idx].longName,
               MAX_LONG_OPER_LEN - 1);
      strncpy (plmnDesc->shrtName,
               operListFixed[idx].shrtName,
               MAX_SHRT_OPER_LEN - 1);
      plmnDesc->mcc = operListFixed[idx].mcc;
      plmnDesc->mnc = operListFixed[idx].mnc;
      return TRUE;
    }
  }

  return FALSE;
}

/*
+-------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)         MODULE  : CMH_MM                  |
|                                 ROUTINE : cmhMM_CnvrtPLMN2INT     |
+-------------------------------------------------------------------+

  PURPOSE : convert the BCD PLMN notation for mcc and mnc into
            integer values.
*/

GLOBAL void cmhMM_CnvrtPLMN2INT( const UBYTE * BCDmcc, const UBYTE * BCDmnc,
                                       SHORT * mccBuf,       SHORT * mncBuf )
{
  SHORT idx;

  /*
   * Convert MCC
   */
  *mccBuf = 0;

  for (idx = 0; idx < SIZE_MCC; idx++)
  {
    *mccBuf = (*mccBuf << 4) + BCDmcc[idx];
  }

  /*
   * Convert MNC
   */
  *mncBuf = 0;

  for (idx = 0; idx < SIZE_MNC; idx++)
  {
    *mncBuf = (*mncBuf << 4) + BCDmnc[idx];
  }
}

/*
+-------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)         MODULE  : CMH_MM                  |
|                                 ROUTINE : cmhMM_CnvrtINT2PLMN     |
+-------------------------------------------------------------------+

  PURPOSE : convert the integer PLMN notation for mcc and mnc into
            BCD representation.
*/

GLOBAL void cmhMM_CnvrtINT2PLMN( SHORT INTmcc, SHORT INTmnc,
                                 UBYTE * mccBuf, UBYTE * mncBuf )
{
  SHORT idx;
  SHORT shift;

  /*
   * Convert MCC
   */
  shift = 0;

  for (idx = SIZE_MCC - 1; idx >= 0; idx--)
  { /*lint -e{702} */
    mccBuf[idx] = (INTmcc >> shift) & 0xf;
    shift += 4;
  }

  /*
   * Convert MNC
   */
  shift = 0;

  for (idx = SIZE_MNC - 1; idx >= 0; idx--)
  { /*lint -e{702} */
    mncBuf[idx] = (INTmnc >> shift) & 0xf;
    shift += 4;
  }
}

/*
+-------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)         MODULE  : CMH_MM                  |
|                                 ROUTINE : cmhMM_GetNregCREGStat   |
+-------------------------------------------------------------------+

  PURPOSE : return CREG status for a not registered state.
*/

GLOBAL T_ACI_CREG_STAT cmhMM_GetNregCREGStat( void )
{
  switch( mmShrdPrm.regMode )
  {
    case( MODE_AUTO ):

#if defined GPRS AND defined (DTI)
      if ( cmhGMM_isClassCG() )
        return(CREG_STAT_NoSearch);
      else
#endif
      {
       /* Depending on the deregistration cause proper CREG state has been sent to the user */
        switch(mmShrdPrm.deregCs)
        {
          case( NREG_LIMITED_SERVICE ):
            return(CREG_STAT_Denied);
          case( NREG_NO_SERVICE ):
             return(CREG_STAT_NoSearch);
          default:
            return(CREG_STAT_Unknown);
        }
      }
    case( MODE_MAN  ):
      return(CREG_STAT_NoSearch);

    default:
      return(CREG_STAT_Unknown);
  }
}

/*
+-------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)         MODULE  : CMH_MM                  |
|                                 ROUTINE : cmhMM_GetNregCMEStat    |
+-------------------------------------------------------------------+

  PURPOSE : return CME error status for a not registered state.
*/

GLOBAL T_ACI_CME_ERR cmhMM_GetNregCMEStat( void )
{
  switch( mmShrdPrm.regStat )
  {
    case( NO_VLD_RS ):
    case( RS_NO_SRV ):

      return( CME_ERR_NoServ );

    case( RS_LMTD_SRV  ):

      return( CME_ERR_LimServ );

    default:

      return( CME_ERR_Unknown );
  }
}

/*
+-------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)         MODULE  : CMH_MM                  |
|                                 ROUTINE : cmhMM_GetOperLstLen     |
+-------------------------------------------------------------------+

  PURPOSE : return CME error status for a not registered state.
*/

GLOBAL USHORT cmhMM_GetOperLstLen( void )
{
  return((sizeof(operListFixed)/sizeof(T_OPER_NTRY_FIXED))-1);
}

/*
+-------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)         MODULE  : CMH_MM                  |
|                                 ROUTINE : cmhMM_CnvrtTrmPCMOpNam  |
+-------------------------------------------------------------------+

  PURPOSE : Convert and terminate PCM long and short operator names.
*/

GLOBAL void cmhMM_CnvrtTrmPCMOpNam( T_OPER_ENTRY *plmnDesc, void *pPCMBuf )
{
  UBYTE len;
  EF_PLMN * pPlmn = (EF_PLMN *)pPCMBuf;

  /* convert and terminate long name */
  for( len = 0;
       len < SIZE_EF_PLMN_LONG AND pPlmn->lngNam[len] NEQ 0xFF;
       len++ )
    ;

  if (len > MAX_LONG_OPER_LEN - 1)
    len = MAX_LONG_OPER_LEN - 1;

  utl_cvtGsmIra( pPlmn->lngNam, len,
        (UBYTE *)plmnDesc->longName, len,
                 CSCS_DIR_GsmToIra );

  plmnDesc->longName[len] = '\0';

  /* convert and terminate short name */
  for( len = 0;
       len < SIZE_EF_PLMN_SHRT AND pPlmn->shrtNam[len] NEQ 0xFF;
       len++ )
    ;

  if (len > MAX_SHRT_OPER_LEN - 1)
    len = MAX_SHRT_OPER_LEN - 1;

  utl_cvtGsmIra( pPlmn->shrtNam, len,
        (UBYTE *)plmnDesc->shrtName, len,
                 CSCS_DIR_GsmToIra );

  plmnDesc->shrtName[len] = '\0';
}

/*
+----------------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)         MODULE  : CMH                              |
|                                 ROUTINE : cmhMM_Ntfy_NtwRegistrationStatus |
+----------------------------------------------------------------------------+

  PURPOSE : report function
*/
#ifdef FF_CPHS
LOCAL BOOL has_roaming_state_changed(T_ACI_CREG_STAT creg)
{
static T_ACI_CREG_STAT last_creg_notified = CREG_STAT_NotPresent;
  UBYTE previous_creg;

  TRACE_EVENT_P2("has_roaming_state_changed() creg: %d, previous creg: %d", creg, last_creg_notified);
  previous_creg = last_creg_notified;

  /* update last_creg_notified */
  last_creg_notified = creg;

  /* status has not changed at all */
  if(creg EQ previous_creg)
    return(FALSE);

  /* Mobile is entering Roaming service: since creg is different from last_creg */
  if(creg EQ CREG_STAT_Roam)
    return(TRUE);

  /* Mobile is not in Roaming service: Check whether this has already been told to the user */
  if(previous_creg EQ CREG_STAT_Roam)
    return(TRUE);

  return(FALSE);
}

LOCAL void cmhMM_cphs_roaming(T_ACI_CREG_STAT creg)
{
  if(has_roaming_state_changed(creg))
  {
    /* Roaming state has changed */
    if(creg EQ CREG_STAT_Roam)
    {
      cphs_roaming_ind(CPHS_ROAMING_ON);
    }
    else if(creg NEQ CREG_STAT_NotPresent)
    {
      cphs_roaming_ind(CPHS_ROAMING_OFF);
    }
  }
}
#endif /* FF_CPHS */

GLOBAL void cmhMM_Ntfy_NtwRegistrationStatus( T_ACI_CREG_STAT creg )
{
  SHORT src;
  T_ACI_P_CREG_GPRS_IND gprs_ind;

  TRACE_FUNCTION ("cmhMM_Ntfy_NtwRegistrationStatus()");

#ifdef FF_CPHS
  cmhMM_cphs_roaming(creg);
#endif /* FF_CPHS */

#if defined (GPRS) AND defined (DTI)
  gprs_ind = gmmShrdPrm.gprs_indicator;  
#else
  gprs_ind = P_CREG_GPRS_Not_Supported; /* ACI-SPR-17218: use ACI type */
#endif

  /* notify GSM change in network status */
  /* +CREG */
  if( creg NEQ CREG_STAT_NotPresent )
  {
    mmShrdPrm.creg_status = creg;

    for( src = 0; src < CMD_SRC_MAX; src++ )
    {
      R_AT( RAT_CREG, src )( creg, mmShrdPrm.lac, mmShrdPrm.cid );
      R_AT( RAT_P_CREG, src )( creg, mmShrdPrm.lac, mmShrdPrm.cid, gprs_ind );      
    }
  }
}

/*
+-------------------------------------------------------------------------------------+
| PROJECT : GSM-PS                MODULE  : CMH                                       |
|                                 ROUTINE : cmhMM_OpCheckName                         |
+-------------------------------------------------------------------------------------+

  PURPOSE : Function for EONS support. Checks if the operator name should be
            read from EF_PNN upon registration or location update.
*/
GLOBAL void cmhMM_OpCheckName()
{
  SHORT mncCur, mccCur;   /* holds converted mnc and mcc of current PLMN */
  SHORT mncOpl, mccOpl;
  T_opl_field *ptrOpl;
  UBYTE i, pnn_rec_num=0;

  TRACE_FUNCTION ("cmhMM_OpCheckName()");

  /* check if PNN is active and allocated on SIM */
  if(psaSIM_ChkSIMSrvSup(SRV_PNN))
  {
    /* check if OPL is active and allocated */
    /* if OPL not activate the first record of PNN holds the operator name of the HPLMN */
    if(!psaSIM_ChkSIMSrvSup(SRV_OPL))
    {
       /* only read PNN if not already present */
      if(mmShrdPrm.PNNLst.plmn.v_plmn != VLD_PLMN)
       {
        /* if HPLMN: this sequence is copied from cmhMM_Registered() */
        cmhMM_CnvrtPLMN2INT( mmShrdPrm.usedPLMN.mcc,
                             mmShrdPrm.usedPLMN.mnc,
                             &mccCur, &mncCur );
        if( cmhSIM_plmn_is_hplmn (mccCur, mncCur))
        {
          cmhMM_OpReadName(1);
        }
      }
      return;
     }
    /* check if the registered PLMN or LAC has changed since last registration */

    if (memcmp (mmShrdPrm.usedPLMN.mcc, mmShrdPrm.PNNLst.plmn.mcc, SIZE_MCC) != 0 ||
        memcmp (mmShrdPrm.usedPLMN.mnc, mmShrdPrm.PNNLst.plmn.mnc, SIZE_MNC) != 0 ||
        mmShrdPrm.lac != mmShrdPrm.PNNLst.lac) 
     {
      ptrOpl = cmhSIM_GetOPL();
      if (ptrOpl->opl_status)
      {
        /* search the OPL list for the new PNN record number      */
        i = 0;
        pnn_rec_num = 0; /* if PNN record number in OPL is 0, no PNN name is available. */
        while (i < ptrOpl->num_rcd)
        {   
          cmhMM_CnvrtPLMN2INT( mmShrdPrm.usedPLMN.mcc,
                             mmShrdPrm.usedPLMN.mnc,
                             &mccCur, &mncCur );

          cmhSIM_getMncMccFrmPLMNsel (ptrOpl->opl_rcd[i].plmn,
                                      &mccOpl, &mncOpl);

          if(cmhSIM_plmn_equal_sim_wc (mccCur, mncCur, mccOpl, mncOpl) AND
             (ptrOpl->opl_rcd[i].lac1 <= mmShrdPrm.lac) AND
             (mmShrdPrm.lac <= ptrOpl->opl_rcd[i].lac2))
          {
            pnn_rec_num = ptrOpl->opl_rcd[i].pnn_rec_num;
            break;
          }
          else 
            i++;
        }
        if (pnn_rec_num != 0)
        {
          /* only read PNN if it is different from the last one saved */
          if (mmShrdPrm.PNNLst.pnn_rec_num != pnn_rec_num)
          {
            cmhMM_OpReadName(pnn_rec_num);
            mmShrdPrm.PNNLst.plmn.v_plmn = INVLD_PLMN;
          }
        }
      }
      else
       TRACE_EVENT("OPL list unavailable");; /* no OPL list available */
    }
  }

/* if CPHS ONS file is not already read then read it */
  if (!pnn_rec_num AND (ONSReadStatus EQ ONS_READ_NOT_DONE))
  {
#ifdef FF_CPHS
    cmhMM_ONSReadName();
#endif
    mmShrdPrm.PNNLst.plmn.v_plmn = INVLD_PLMN;
  }
}
/*
+-------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)    MODULE  : CMH_SMSF                     |
| STATE   : code             ROUTINE : cmhMM_OpReadName             |
+-------------------------------------------------------------------+

  PURPOSE : Sends a SIM read request

  RESULT: has an error occured ?
  Returns: FALSE if primitive has been sent and TRUE if not
*/
GLOBAL BOOL cmhMM_OpReadName(UBYTE rcd)
{
  TRACE_FUNCTION ("cmhMM_OpReadName()");

  if (cmhSIM_ReadRecordEF (CMD_SRC_NONE,
                           AT_CMD_NONE,
                           SIM_PNN,
                           rcd,
                           NOT_PRESENT_8BIT,
                           NULL,
                           cmhMM_OpReadNameCb) EQ AT_FAIL)
  {
    TRACE_EVENT("FATAL ERROR");
    return(TRUE);
  }
  return(FALSE);
}

/*
+---------------------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)    MODULE  : CMH_SMSF                                   |
| STATE   : code             ROUTINE : cmhMM_OpReadNameCb                         |
+---------------------------------------------------------------------------------+

  PURPOSE : Call back for SIM retrieval of EF_PNN.
*/
GLOBAL void cmhMM_OpReadNameCb(SHORT table_id)
{
  UBYTE *data;
  TRACE_FUNCTION ("cmhMM_OpReadNameCb()");

  data = simShrdPrm.atb[table_id].exchData;

  /* Decode and copy PNN record data to PNN list*/
  /*------------------------------------------*/

  if (simShrdPrm.atb[table_id].errCode EQ SIM_NO_ERROR) /* Process only if file is read */
  {

    mmShrdPrm.PNNLst.plmn = mmShrdPrm.usedPLMN;
    mmShrdPrm.PNNLst.lac = mmShrdPrm.lac;
    mmShrdPrm.PNNLst.pnn_rec_num = simShrdPrm.atb[table_id].recNr;

    if (*data++ EQ PNN_LONG_NAME_IEI)
    {
        mmShrdPrm.PNNLst.long_len  = (*data++)-1; /* substract dcs byte from lem */
        mmShrdPrm.PNNLst.long_ext_dcs = *data++;
        memcpy (mmShrdPrm.PNNLst.long_name,
                data,
                MINIMUM(mmShrdPrm.PNNLst.long_len, sizeof(mmShrdPrm.PNNLst.long_name)));
        data += mmShrdPrm.PNNLst.long_len;
  
        /* PNN_SHORT_NAME_IEI is an optional field */
        if (*data++ EQ PNN_SHORT_NAME_IEI)
        {
            mmShrdPrm.PNNLst.shrt_len = (*data++)-1; /* substract dcs byte from len */
            mmShrdPrm.PNNLst.shrt_ext_dcs = *data++;
            memcpy (mmShrdPrm.PNNLst.shrt_name,
                    data,
                    MINIMUM(mmShrdPrm.PNNLst.shrt_len, sizeof(mmShrdPrm.PNNLst.shrt_name)));
        }
        else
        {
            mmShrdPrm.PNNLst.shrt_len = 0;
        }

        mmShrdPrm.PNNLst.next = NULL;
    }
    else /* PNN_LONG_NAME_IEI is a mandatory field in PNN, if not present, PNN is invalid */
    {
        /* PNN record number 0 indicates no PNN name is available */
        mmShrdPrm.PNNLst.pnn_rec_num = 0;
    }
  }
  cmhMM_Registered ();
  simShrdPrm.atb[table_id].ntryUsdFlg = FALSE;
}

/*
+---------------------------------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)    MODULE  : CMH_SMSF                                               |
| STATE   : code             ROUTINE : cmhMM_OpSetPNNLst                                      |
+---------------------------------------------------------------------------------------------+

  PURPOSE : starts reading of EF_PNN from SIM and creates PNN list for manual PLMN selection.
*/
GLOBAL void cmhMM_OpSetPNNLst()
{
  T_opl_field *ptrOpl;
  UBYTE i,j, pnn_rec_num;

  TRACE_FUNCTION ("cmhMM_OpSetPNNLst()");
 
  ptrOpl = cmhSIM_GetOPL();
  if (ptrOpl->opl_status)
  {
    mmShrdPrm.pnn_read_cnt = 0;
    /* search the OPL list for the PNN record number
       for every entry in the network search lists */    

    /* OPL list is searched for every entry in the network search lists */
    for (j=0; j < MAX_PLMN_ID; j++) 
    {
      /* check if entry in PLMNLst is valid */
      if( mmShrdPrm.PLMNLst[j].v_plmn != INVLD_PLMN )
      {
        pnn_rec_num = 0; /* if PNN record number in OPL is 0, no PNN name is available. */
        i = 0;
        ptrOpl = cmhSIM_GetOPL();
        while (i < ptrOpl->num_rcd) /* search OPL list for PLMN nr. j */
        {
          SHORT bcch_mcc, bcch_mnc;
          SHORT sim_mcc, sim_mnc;

          cmhMM_CnvrtPLMN2INT (mmShrdPrm.PLMNLst[j].mcc, mmShrdPrm.PLMNLst[j].mnc,
                               &bcch_mcc, &bcch_mnc);
          cmhSIM_getMncMccFrmPLMNsel (ptrOpl->opl_rcd[i].plmn,
                                      &sim_mcc, &sim_mnc);
          if (cmhSIM_plmn_equal_sim (bcch_mcc, bcch_mnc, sim_mcc, sim_mnc) AND
              ((ptrOpl->opl_rcd[i].lac1 EQ 0x0000 AND
                ptrOpl->opl_rcd[i].lac2 EQ 0xFFFE) OR
               (ptrOpl->opl_rcd[i].lac1 <= mmShrdPrm.LACLst[j] AND
                mmShrdPrm.LACLst[j] <= ptrOpl->opl_rcd[i].lac2)))
          {
            pnn_rec_num = ptrOpl->opl_rcd[i].pnn_rec_num;
            break;
          }
          else 
            i++;
        }
        if (pnn_rec_num != 0)
        {
          if (cmhSIM_ReadRecordEF (CMD_SRC_NONE,
                                   AT_CMD_NONE,
                                   SIM_PNN,
                                   pnn_rec_num, /* the PNN rec. number is read */
                                   NOT_PRESENT_8BIT,
                                   NULL,
                                   cmhMM_OpExtractNameCB) EQ AT_FAIL)
          {
             TRACE_EVENT("FATAL ERROR");
             break; /* try to continue with the next one instead of return
                       since otherwise AT+COPS=? will hang if cmhMM_NetworkLst() is not called! */
          }
          /* in cmhMM_OpExtractNameCB the latest received PNN is placed right after the PNN-list head */
          /* pnn_nr array is used for finding the associated plmn and lac in cmhMM_OpExtractNameCB. */
          mmShrdPrm.pnn_nr[j] = pnn_rec_num;
          mmShrdPrm.pnn_read_cnt++;
        }
        else
        {
          /* name is to be taken from other sources, 3G TS 22.101 */
        }
      }
      else
      {
        break; /* and Invalid PLMN indicates the end of the list. */
      }
    }
    if (mmShrdPrm.pnn_read_cnt EQ 0)  /* nothing processed? */
    {
      if(( mmEntStat.curCmd EQ AT_CMD_COPS) OR ( mmEntStat.curCmd EQ AT_CMD_P_COPS) )
        cmhMM_NetworkLst();             /* then print list anyway, otherwise AT+COPS=? will fail. */
    }
  }
  else
  {
    if(( mmEntStat.curCmd EQ AT_CMD_COPS) OR ( mmEntStat.curCmd EQ AT_CMD_P_COPS) )
      cmhMM_NetworkLst();             /* then print list anyway, otherwise AT+COPS=? will fail. */
  }
}

/*
+-------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)    MODULE  : CMH_SMSF                     |
| STATE   : code             ROUTINE : cmhMM_OpExtractName          |
+-------------------------------------------------------------------+

  PURPOSE : decodes EF_PNN record read from SIM.
*/
GLOBAL void cmhMM_OpExtractNameCB(SHORT table_id)
{
  UBYTE *data;
  UBYTE i;
  T_pnn_name *newPNN;

  TRACE_FUNCTION ("cmhMM_OpExtractNameCB()");

  data = simShrdPrm.atb[table_id].exchData;

  if (*data++ EQ PNN_LONG_NAME_IEI)
  {
    MALLOC(newPNN,sizeof(T_pnn_name));
    newPNN->next = NULL;

    newPNN->pnn_rec_num = simShrdPrm.atb[table_id].recNr;
  
    /*find the associated PLMN and LAC for this PNN */
    i = 0;
    while (i < MAX_PLMN_ID)
    {
      if (mmShrdPrm.pnn_nr[i] EQ newPNN->pnn_rec_num)
      {
        newPNN->plmn = mmShrdPrm.PLMNLst[i];
        newPNN->lac = mmShrdPrm.LACLst[i];
        break;
      }
      else
        i++;
    }

    newPNN->long_len = (*data++)-1;   /* substract dcs byte */
    newPNN->long_ext_dcs = *data++;
    memcpy (newPNN->long_name,
            data,
            MINIMUM(newPNN->long_len, sizeof(newPNN->long_name)));
    data += newPNN->long_len;

     /*----- IEI PNN short name ------*/
    if (*data++ EQ PNN_SHORT_NAME_IEI)
    {
      newPNN->shrt_len  = (*data++)-1;  /* substract dcs byte */
      newPNN->shrt_ext_dcs = *data++;
      memcpy (newPNN->shrt_name,
              data,
              MINIMUM(newPNN->shrt_len,sizeof(newPNN->shrt_name)));
    } 
    else
    {
      newPNN->shrt_len = 0;
    }

    /* insert new element right after the static stored PNN name.  */
    newPNN->next = mmShrdPrm.PNNLst.next;
    mmShrdPrm.PNNLst.next = newPNN; 
  }

  mmShrdPrm.pnn_read_cnt--; /* reading of PNN records completed, network list is prepared */
  if (mmShrdPrm.pnn_read_cnt EQ 0)
    cmhMM_NetworkLst ();    

  /* Deallocation of elements in linked list PNN */
  // As you see, you see nothing.
  // ============================

  // Immediate deallocation is not possible as someone could have
  // the idea to do a AT+COPS=? and immediately thereafter a 
  // AT+COPS=1,0,"T-Mobile D" or AT+COPS=1,1,"TMO D"
  // A workaround could be to free everything not present in the actual
  // network list. Until now only a theoretical problem. For further study.

  // There is a second problem that this MM EONS extension list is not
  // affected by SAT changes of the respective files. Until now also 
  // only theoretical and for further study.

  // And there is a third problem. This is, there exist 2 solutions
  // in the PS for EONS, the first one sitting in SIM related ACI around
  // the T_pnn data structure and the second one sitting in MM related ACI
  // around the T_pnn_name data structure. Two solutions for the same
  // problem.

  // => We should consider in the future to remove the implementation around
  // T_pnn_name and to enhance the T_pnn in SIM to end up in only one
  // implementation. This would improve the PS and save ROM.
  // Maybe the increasement of PNN_MAX_RECORDS to MAX_PLMN_ID + 1 or
  // something somewhat bigger will already do, otherwise an 
  // intelligent entry replacement (aging) algorithm has to be introduced.
  // For further study.

  simShrdPrm.atb[table_id].ntryUsdFlg = FALSE;

}

/*
+-------------------------------------------------------------------------------------+
| PROJECT : GSM-PS                MODULE  : CMH                                       |
|                                 ROUTINE : cmhMM_OpUpdateName                        |
+-------------------------------------------------------------------------------------+

  PURPOSE : Function for EONS support. Updates the operator name by reading EF_PNN.
  RETURNS: false if PS busy with reading files (primitive has been sent)
  TRUE if PS can go on with the updating
*/
GLOBAL BOOL cmhMM_OpUpdateName()
{
  SHORT mncCur, mccCur;   /* holds converted mnc and mcc of current PLMN */
  T_opl_field *ptrOpl;
  UBYTE i, pnn_rec_num;

  TRACE_FUNCTION ("cmhMM_OpUpdateName()");

  /* check if OPL is active and allocated */
  /* if OPL not activate the first record of PNN holds the operator name of the HPLMN */
  if(!psaSIM_ChkSIMSrvSup(SRV_OPL))
  {
    cmhMM_CnvrtPLMN2INT( mmShrdPrm.usedPLMN.mcc,
                         mmShrdPrm.usedPLMN.mnc,
                         &mccCur, &mncCur );
    if( cmhSIM_plmn_is_hplmn (mccCur, mncCur))
    {
      return(cmhMM_OpReadName(1));
    }
    return(TRUE); /* no primitive has been sent to SIM */
  }
  else /* OPL is active and allocated */
  {
    ptrOpl = cmhSIM_GetOPL();
    if (ptrOpl->opl_status)
    {
      /* search the OPL list for the new PNN record number      */
      i = 0;
      pnn_rec_num = 0; /* if PNN record number in OPL is 0, no PNN name is available. */
      while (i < ptrOpl->num_rcd)
      {
        SHORT bcch_mcc, bcch_mnc;
        SHORT sim_mcc, sim_mnc;

        cmhMM_CnvrtPLMN2INT (mmShrdPrm.usedPLMN.mcc, mmShrdPrm.usedPLMN.mnc,
                             &bcch_mcc, &bcch_mnc);
        cmhSIM_getMncMccFrmPLMNsel (ptrOpl->opl_rcd[i].plmn,
                                    &sim_mcc, &sim_mnc);
        if (cmhSIM_plmn_equal_sim (bcch_mcc, bcch_mnc, sim_mcc, sim_mnc) AND 
            (ptrOpl->opl_rcd[i].lac1 <= mmShrdPrm.lac) AND
            (mmShrdPrm.lac <= ptrOpl->opl_rcd[i].lac2))
        {
          pnn_rec_num = ptrOpl->opl_rcd[i].pnn_rec_num;
          break;
        }
        else 
          i++;
      }
      if (pnn_rec_num != 0)
      {
        return(cmhMM_OpReadName(pnn_rec_num));
      }
    }
    else
     TRACE_EVENT("OPL list unavailable");; /* no OPL list available */
  }
  return(TRUE); /* no primitive has been sent to SIM */
}

/*
+-------------------------------------------------------------------------------------+
| PROJECT : GSM-PS                MODULE  : CMH                                       |
|                                 ROUTINE : cmhMM_GetCmerSettings                     |
+-------------------------------------------------------------------------------------+
*/
GLOBAL void cmhMM_GetCmerSettings(T_ACI_CMD_SRC srcId, T_ACI_MM_CMER_VAL_TYPE *sCmerSettings)
{
  sCmerSettings->sCmerModeParam = cmhPrm[srcId].mmCmdPrm.sIndicationParam.sMmCMERSettings.sCmerModeParam;
  sCmerSettings->sCmerIndParam  = cmhPrm[srcId].mmCmdPrm.sIndicationParam.sMmCMERSettings.sCmerIndParam;
  sCmerSettings->sCmerBfrParam  = cmhPrm[srcId].mmCmdPrm.sIndicationParam.sMmCMERSettings.sCmerBfrParam;
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)         MODULE  : CMH_MMF                  |
| STATE   : code                  ROUTINE : cmhMM_ChkIgnoreECC       |
+--------------------------------------------------------------------+

  PURPOSE : This function is used to check if ECC should be set
            as a normal call. (Issue 17570)
  RETURN: TRUE - if further ECC checking should be ignored and call must be placed as normal
                FALSE - if ECC checking should be continued
*/

GLOBAL BOOL cmhMM_ChkIgnoreECC(CHAR *dialStr)
{
  int i=0;
  SHORT mnc, mcc;

  /* remove any CLIR suppression/invocation prior checking for ECC */
  if (!strncmp( dialStr, "*31#", 4) OR
      !strncmp( dialStr, "#31#", 4) )
  {
    dialStr+=4;                  /* skip CLIR supression/invocation digits */
    if ( *dialStr EQ '\0' )
    {
      return( FALSE );  /* end already reached? */
    }
  }

  if(mmShrdPrm.usedPLMN.v_plmn EQ VLD_PLMN)
  {
    cmhMM_CnvrtPLMN2INT( mmShrdPrm.usedPLMN.mcc, mmShrdPrm.usedPLMN.mnc, &mcc, &mnc );

    while (ECCIgnoreTable[i].mcc NEQ -1 AND ECCIgnoreTable[i].mnc NEQ -1 )
    {
      if ( ECCIgnoreTable[i].mcc EQ mcc AND
          (ECCIgnoreTable[i].mnc EQ mnc OR ECCIgnoreTable[i].mnc EQ ALL_MNC) )
      {
        if(!strcmp(ECCIgnoreTable[i].ecc, dialStr))
        {
          TRACE_EVENT("cmhCC_ChkIgnoreECC(): no ECC but normal call detected");
          return (TRUE);
        }
      }
      i++;
    }
  }
  return (FALSE);
}


/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)         MODULE  : CMH_MMF                  |
| STATE   : code                  ROUTINE : cmhMM_OperatorSelect       |
+--------------------------------------------------------------------+

  PURPOSE : 

  This function encapsulates the common CMH functionality of 
  sAT_PlusCOPS(),   sAT_PercentCOPS(), and sAT_PercentNRG(). 

  The body of this function is based on the former sAT_PercentNRG().
  
*/

GLOBAL T_ACI_RETURN cmhMM_OperatorSelect(T_ACI_CMD_SRC srcId,
                                         T_ACI_NRG_RGMD regMode,
                                         T_ACI_NRG_SVMD srvMode,
                                         T_ACI_NRG_FRMT oprFrmt,
                                         CHAR          *opr)
{
  T_MM_CMD_PRM  * pMMCmdPrm;  /* points to MM command parameters */
  T_SIM_SET_PRM * pSIMSetPrm; /* points to SIM parameter set */
  T_OPER_ENTRY    plmnDesc;   /* points to PLMN description */
  T_ACI_RETURN    retVal=AT_FAIL;  /* holds return value */
  BOOL            found;      /* result of cmhMM_FindXXX() */    
  UBYTE rm;                   /* holds converted registration mode */

  TRACE_FUNCTION ("cmhMM_OperatorSelect()");

/*
 *-------------------------------------------------------------------
 * check command source
 *-------------------------------------------------------------------
 */
  if(!cmh_IsVldCmdSrc (srcId))
  {
    mmEntStat.curCmd  = AT_CMD_NONE; /*Unset the Current Command */
    ACI_ERR_DESC( ACI_ERR_CLASS_Ext, EXT_ERR_Parameter );
    return( AT_FAIL );
  }

  pMMCmdPrm  = &cmhPrm[srcId].mmCmdPrm;
  pSIMSetPrm = &simShrdPrm.setPrm[srcId];
  mmShrdPrm.regModeBeforeAbort = mmShrdPrm.regMode;

/*
 *-------------------------------------------------------------------
 * process the regMode parameter
 *-------------------------------------------------------------------
 */

  switch( regMode )
  {
    case( NRG_RGMD_Auto   ): rm = MODE_AUTO; break;
    case( NRG_RGMD_Manual ):
    case( NRG_RGMD_Both   ): rm = MODE_MAN;  
      if(!cmhSIM_isplmnmodebit_set())
      {
        mmEntStat.curCmd  = AT_CMD_NONE; /*Unset the Current Command */
        ACI_ERR_DESC( ACI_ERR_CLASS_Cme, CME_ERR_OpNotAllow );
        return( AT_FAIL );
      }
      break;
    case( NRG_RGMD_Dereg   ): rm = MODE_AUTO;  break; 

    default:
      mmEntStat.curCmd  = AT_CMD_NONE; /*Unset the Current Command */
      ACI_ERR_DESC( ACI_ERR_CLASS_Ext, EXT_ERR_Parameter );
      return( AT_FAIL );
  }


/*
 *-------------------------------------------------------------------
 * process the srvMode parameter
 *-------------------------------------------------------------------
 */

  switch( srvMode )
  {
    /*
     *---------------------------------------------------------------
     * setting of registration mode only. This is only used by %NRG. It is obsolete and
     * should be removed at some point in the future.
     *---------------------------------------------------------------
     */
    case( NRG_SVMD_SetRegModeOnly ):

      /*
        * Unset the Current Command  to match exactly what sAT_PercentNRG()
        * used to do.
      */
      mmEntStat.curCmd  = AT_CMD_NONE; 
      mmShrdPrm.regMode = rm;
      mmShrdPrm.regModeAutoBack = FALSE;

      /*
       * Documentation (8415.052.00.002) says:
       *
       * "<srvMode>=3 can be used to change the behaviour of registration
       * in case of a loss of coverage. If connection to the operator is
       * lost and <regMode> was set to manual, ME tries to register the previous
       * operator automatically.". This is not exactly what is done here.
       * What is done is that the registration mode is set to automatic in MM.
       * The main difference is that for phase 2+ the HPLMN search period
       * will be evaluated by RR and the HPLMN may be reselected after 30 minutes,
       * even without any loss of coverage.
       */


#ifdef  GPRS
      if( psaG_MM_CMD_SET_REGMD ( rm ) < 0 )  /* restore former registration mode*/
#else
      if( psaMM_SetRegMode ( rm ) < 0 ) /* set registration mode */
#endif
      {
        TRACE_EVENT( "FATAL RETURN psaMM_SetRegMode in %%NRG" );
        ACI_ERR_DESC( ACI_ERR_CLASS_Ext, EXT_ERR_Internal );
        retVal = AT_FAIL;
      }
      else
        retVal = AT_CMPL;
      break;

    /*
     *---------------------------------------------------------------
     * registration to full service
     *---------------------------------------------------------------
     */
    case( NRG_SVMD_Full ):

      /* For the %NRG case, only if SIM data is available we can start a registration attempt 
       * But the COPS commands dont require that.
       */
     
      if(( simShrdPrm.SIMStat EQ SS_OK AND
          simShrdPrm.PINStat EQ PS_RDY )
          OR (mmEntStat.curCmd NEQ AT_CMD_NRG))
      {
        switch( regMode )
        {
          case( NRG_RGMD_Auto ):  /* automatic registration */

            mmShrdPrm.regModeAutoBack = FALSE;
            mmShrdPrm.regMode = MODE_AUTO;
            mmEntStat.entOwn  = mmShrdPrm.owner = srcId;

#ifdef GPRS 
      /*   Taken from the former sAT_PlusCOPS()
       *    COPS will be request an only GSM registration.
       *    If the ME is an only GPRS mobile, then it is impossible to request only GSM.
       *    This patch will be eliminate an error in this situation.
       *
       *    brz, 02.07.01
       */
      if ( cmhGMM_class_eq_CG() EQ TRUE )
      {
         mmEntStat.curCmd  = AT_CMD_NONE; /*Unset the Current Command */
         return AT_CMPL;
      }  
#endif


#ifdef  GPRS
            if( psaG_MM_CMD_REG ( ) < 0 )  /* register to network */
#else
            if( psaMM_Registrate () < 0 )  /* register to network */
#endif
            {
              TRACE_EVENT( "FATAL RETURN psaMM_Registrate in cmhMM_OperatorSelect()" );
              ACI_ERR_DESC( ACI_ERR_CLASS_Ext, EXT_ERR_Internal );
              retVal = AT_FAIL;
              break;
            }

            cmhMM_Ntfy_NtwRegistrationStatus(CREG_STAT_Search);

            retVal = AT_EXCT;
            break;

          case( NRG_RGMD_Manual ): /* manual registration               */
          case( NRG_RGMD_Both   ): /* manual followed by automatic reg. */

            mmShrdPrm.regModeAutoBack = (regMode EQ NRG_RGMD_Both);

            /*
              * The following code is for %COPS=1 and %NRG=1. 
              * It will get the IMSI and last registered PLMN from FFS.
              * If the IMSI has changed or no information is available in FFS, 
              * it will tell MM to register to the operator stored in EF(LOCI). 
              * If the IMSI hasn't changed, it will manually register to the Operator stored in FFS.
              *  
              */
            if( !opr OR !*opr) 
            {
                UBYTE mcc[SIZE_MCC];
                UBYTE mnc[SIZE_MNC];
                UBYTE IMSI[MAX_IMSI-1];
                char tempIMSI[MAX_IMSI] = {'\0'};
                BOOL readFromLOCI =FALSE;
      
                
                mmShrdPrm.slctPLMN.v_plmn = VLD_PLMN;
              
               /* Get   the IMSI and last registered PLMN from FFS. 
                 * Compare the IMSI stored in FFS to the current one. If the same, copy MCC and MNC
                 * to pMMSetPrm -> slctPLMN. This will select this network manually.
                 */

                if  (cmhMM_OperatorReadFromFFS(mcc,mnc,IMSI))
                {
                   TRACE_EVENT("Operator and IMSI succesfully read from FFS!");
                   if (!memcmp(IMSI,simShrdPrm.imsi.field,MAX_IMSI-1))
                   {
                       memcpy(mmShrdPrm.slctPLMN.mcc,  mcc,SIZE_MCC);
                       memcpy(mmShrdPrm.slctPLMN.mnc,  mnc,SIZE_MNC);                  
                   }
                   else
                   {
                      readFromLOCI = TRUE;
                   }  
                }
                else
                   readFromLOCI = TRUE;
                  /* If the SIM has changed or FFS cannot be read, read EF(LOCI) from the SIM.
                  /* This wil now lead to a PLMN_RES with MNC,MCC=0xFFF: */
                  if (readFromLOCI)
                  {
                     psaSIM_cnvrtIMSI2ASCII(tempIMSI);
                     psaSIM_decodeIMSI(IMSI,simShrdPrm.imsi.c_field,tempIMSI);
                     
                     cmhMM_CnvrtINT2PLMN( 0xFFF,
                                   0xFFF,
                                   mmShrdPrm.slctPLMN.mcc,
                                   mmShrdPrm.slctPLMN.mnc );
                  }
             }
            else
            {
              switch( oprFrmt )
              {
                case( NRG_FRMT_Long ):
                  found = cmhMM_FindName (&plmnDesc, opr, COPS_FRMT_Long);
                  break;

                case( NRG_FRMT_Short ):
                  found = cmhMM_FindName (&plmnDesc, opr, COPS_FRMT_Short);
                  break;

                case( NRG_FRMT_Numeric ):
                  found = cmhMM_FindNumeric(&plmnDesc, opr);
                  break;

                default:
                  found = FALSE;
                  break;
              }

              if (!found)
              {
                mmEntStat.curCmd = AT_CMD_NONE; /* Unset the Current Command */
                ACI_ERR_DESC( ACI_ERR_CLASS_Ext, EXT_ERR_Parameter );
                return( AT_FAIL );
              }

              mmShrdPrm.slctPLMN.v_plmn = VLD_PLMN;
              cmhMM_CnvrtINT2PLMN( plmnDesc.mcc, /*lint -e644 plmnDesc may not have been initialized*/
                                   plmnDesc.mnc,
                                   mmShrdPrm.slctPLMN.mcc,
                                   mmShrdPrm.slctPLMN.mnc );
            }
            mmShrdPrm.regMode = MODE_MAN;

            mmEntStat.entOwn  = mmShrdPrm.owner = srcId;

#ifdef  GPRS
            if( psaG_MM_CMD_NET_SEL ( ) < 0 )  /* register to network */
#else
            if( psaMM_NetSel () < 0 )  /* register to network */
#endif
            {
              TRACE_EVENT( "FATAL RETURN psaMM_NetSel in %%NRG" );
              ACI_ERR_DESC( ACI_ERR_CLASS_Ext, EXT_ERR_Internal );
              retVal = AT_FAIL;
              break;
            }

            cmhMM_Ntfy_NtwRegistrationStatus(CREG_STAT_Search);

            retVal = AT_EXCT;
            break;
        }
      }
      /* 
       * No SIM data is available, activate SIM 
       * The following block of code was only available in the former sAT_PercentNRG()
       * Therefore, it will only be executed by the %NRG command.
       */
      else
      {
        /* check SIM entity status */
        if( simEntStat.curCmd NEQ AT_CMD_NONE )

          return( AT_BUSY );

        /* prepare PLMN desc for later use */
        if( regMode EQ NRG_RGMD_Manual )
        {
          if( ! opr )
          {
            mmEntStat.curCmd  = AT_CMD_NONE; /*Unset the Current Command */
            ACI_ERR_DESC( ACI_ERR_CLASS_Ext, EXT_ERR_Parameter );
            return( AT_FAIL );
          }

          switch( oprFrmt )
          {
            case( NRG_FRMT_Long ):
              found = cmhMM_FindName (&plmnDesc, opr, COPS_FRMT_Long);
              break;

            case( NRG_FRMT_Short ):
              found = cmhMM_FindName (&plmnDesc, opr, COPS_FRMT_Short);
              break;

            case( NRG_FRMT_Numeric):
              found = cmhMM_FindNumeric (&plmnDesc, opr);
              break;

            default:
              found = FALSE;
              break;
          }

          if (!found)
          {
            mmEntStat.curCmd = AT_CMD_NONE; /* Unset the Current Command */
            ACI_ERR_DESC( ACI_ERR_CLASS_Ext, EXT_ERR_Parameter );
            return( AT_FAIL );
          }

          mmShrdPrm.slctPLMN.v_plmn = VLD_PLMN;
          cmhMM_CnvrtINT2PLMN( plmnDesc.mcc,
                               plmnDesc.mnc,
                               mmShrdPrm.slctPLMN.mcc,
                               mmShrdPrm.slctPLMN.mnc );
        }

        pSIMSetPrm -> actProc = SIM_INITIALISATION;

        simEntStat.curCmd = AT_CMD_NRG;
        simEntStat.entOwn = simShrdPrm.owner = srcId;

        if( psaSIM_ActivateSIM() < 0 )   /* activate SIM card */
        {
          TRACE_EVENT( "FATAL RETURN psaSIM in %%NRG" );
          ACI_ERR_DESC( ACI_ERR_CLASS_Ext, EXT_ERR_Internal );
          retVal =  AT_FAIL;
          break;
        }

        retVal = AT_EXCT;
      }
      break;

    /*
     *---------------------------------------------------------------
     * registration to limited service.
     * The COPS commands will use this for deregistration
     *---------------------------------------------------------------
     */

    case( NRG_SVMD_Limited ):

      mmShrdPrm.regModeAutoBack = FALSE;

      switch( mmShrdPrm.regStat )
      {
        case( RS_LMTD_SRV ):

          mmEntStat.curCmd  = AT_CMD_NONE; /*Unset the Current Command */
          retVal = AT_CMPL;
          break;

        case( RS_NO_SRV ):
        case( NO_VLD_RS ):
          /*
           * if SIM data is available, do not destory it in ACI.
           * It may be needed later
           */
          if(( simShrdPrm.SIMStat EQ SS_OK AND
              simShrdPrm.PINStat EQ PS_RDY ) OR (mmEntStat.curCmd NEQ AT_CMD_NRG))
          {

           /* flag a pending request. It only applies to the %NRG case */
            if (mmEntStat.curCmd EQ AT_CMD_NRG) 
              regReqPnd = TRUE;                

            mmEntStat.entOwn  = mmShrdPrm.owner = srcId;

#ifdef  GPRS
            mmShrdPrm.nrgCs   = GMMREG_DT_COMB;
            if( psaG_MM_CMD_DEREG ( mmShrdPrm.nrgCs ) < 0 )  /* deregister from network */
#else
            mmShrdPrm.nrgCs   = CS_SIM_REM;
            if( psaMM_DeRegistrate () < 0 )  /* deregister from network */
#endif  /* GPRS */
            {
              TRACE_EVENT( "FATAL RETURN psaMM_DeRegistrate in %%NRG" );
              ACI_ERR_DESC( ACI_ERR_CLASS_Ext, EXT_ERR_Internal );
              retVal = AT_FAIL;
            }
            else
            {
              /* Next line commented out HM 28.08.00 */
              /* simShrdPrm.SIMStat = NO_VLD_SS; */  /* no SIM data available */
              retVal = AT_EXCT;
            }
          }
          /* 
           * No SIM data is available, try  a registration to limited service.
           * The following block of code was only available in the former sAT_PercentNRG()
           * Therefore, it will only be executed by the %NRG command.
           */
          else
          {
            mmShrdPrm.regMode = MODE_AUTO;
            mmEntStat.entOwn  = mmShrdPrm.owner = srcId;
#ifdef  GPRS
            if( psaG_MM_CMD_REG ( ) < 0 )  /* register to network */
#else
            if( psaMM_Registrate () < 0 )  /* register to network */
#endif
            {
              TRACE_EVENT( "FATAL RETURN psaMM_Registrate in %%NRG" );
              ACI_ERR_DESC( ACI_ERR_CLASS_Ext, EXT_ERR_Internal );
              retVal = AT_FAIL;
            }
            else
            {
              retVal = AT_EXCT;
            }
          }
          break;

        case( RS_FULL_SRV ):

          mmEntStat.entOwn  = mmShrdPrm.owner = srcId;

#ifdef  GPRS
          mmShrdPrm.nrgCs   = GMMREG_DT_COMB;
          if( psaG_MM_CMD_DEREG ( mmShrdPrm.nrgCs ) < 0 )  /* deregister from network */
#else
          mmShrdPrm.nrgCs   = CS_SIM_REM;
          if( psaMM_DeRegistrate () < 0 )  /* deregister from network */
#endif
          {
            TRACE_EVENT( "FATAL RETURN psaMM_Deregistrate in %%NRG" );
            ACI_ERR_DESC( ACI_ERR_CLASS_Ext, EXT_ERR_Internal );
            retVal = AT_FAIL;
            break;
          }

          /* Next line commented out HM 28.08.00 */
          /* simShrdPrm.SIMStat = NO_VLD_SS; */  /* no SIM data available */
          retVal = AT_EXCT;
          break;
      }
      break;

    /*
     *---------------------------------------------------------------
     * registration to no service. Only used by %NRG
     *---------------------------------------------------------------
     */
     
    case( NRG_SVMD_NoSrv ):

      mmShrdPrm.regModeAutoBack = FALSE;

      mmEntStat.entOwn  = mmShrdPrm.owner = srcId;

#ifdef  GPRS
      mmShrdPrm.nrgCs   = GMMREG_DT_COMB;
      if( psaG_MM_CMD_DEREG ( mmShrdPrm.nrgCs ) < 0 )  /* deregister from network */
#else
      mmShrdPrm.nrgCs   = CS_POW_OFF;
      if( psaMM_DeRegistrate () < 0 )  /* deregister from network */
#endif
      {
        TRACE_EVENT( "FATAL RETURN psaMM_Deregistrate in %%NRG" );
        ACI_ERR_DESC( ACI_ERR_CLASS_Ext, EXT_ERR_Internal );
        retVal = AT_FAIL;
        break;
      }

      retVal = AT_EXCT;
      break;

    default:
      mmEntStat.curCmd  = AT_CMD_NONE; /*Unset the Current Command */
      ACI_ERR_DESC( ACI_ERR_CLASS_Ext, EXT_ERR_Parameter );
      return( AT_FAIL );
  }

/*
 *-------------------------------------------------------------------
 * update NRG parameters
 *-------------------------------------------------------------------
 */
if (mmEntStat.curCmd  EQ AT_CMD_NRG)
{
  pMMCmdPrm -> NRGsrvMode = srvMode;
  pMMCmdPrm -> NRGregMode = regMode;
  pMMCmdPrm -> NRGoprFrmt = oprFrmt;
}

/*
 *-------------------------------------------------------------------
 * log command execution
 *-------------------------------------------------------------------
 */
#if defined SMI OR defined MFW OR defined FF_MMI_RIV
  {
  T_ACI_CLOG      cmdLog;     /* holds logging info */

  cmdLog.atCmd                = mmEntStat.curCmd;
  cmdLog.cmdType              = CLOG_TYPE_Set;
  cmdLog.retCode              = retVal;
  cmdLog.cId                  = ACI_NumParmNotPresent;
  cmdLog.sId                  = ACI_NumParmNotPresent;

  if (mmEntStat.curCmd  EQ AT_CMD_NRG)
  {
    cmdLog.cmdPrm.sNRG.srcId    = srcId;
    cmdLog.cmdPrm.sNRG.regMode  = regMode;
    cmdLog.cmdPrm.sNRG.srvMode  = srvMode;
    cmdLog.cmdPrm.sNRG.oprFrmt  = oprFrmt;
    cmdLog.cmdPrm.sNRG.opr      = opr;
  }
  else /*+COPS and %COPS. At the moment, the same message sent by both */
  {
     cmdLog.cmdPrm.sCOPS.srcId   = srcId;
     cmdLog.cmdPrm.sCOPS.mode    = mmShrdPrm.COPSmode;
     cmdLog.cmdPrm.sCOPS.format  = pMMCmdPrm -> COPSfrmt;
     cmdLog.cmdPrm.sCOPS.oper    = opr;
  }

  rAT_PercentCLOG( &cmdLog );
  }
#endif

  return( retVal );


}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)         MODULE  : CMH_MMF                  |
| STATE   : code                  ROUTINE : cmhMM_OperatorQuery      |
+--------------------------------------------------------------------+

  PURPOSE : 

  This function encapsulates the common CMH functionality of 
  qAT_PlusCOPS(),   qAT_PercentCOPS(), and qAT_PercentNRG(). It basically gets the 
  Network Operator name based on the format parameter.
  
*/


GLOBAL void cmhMM_OperatorQuery( T_ACI_CMD_SRC srcId,
                                    T_ACI_COPS_FRMT format,
                                    CHAR           *oper)
{
  SHORT        mcc, mnc;    /* holds coverted values for mcc and mnc */
  T_OPER_ENTRY plmnDesc;    /* entry of operator list */
  BOOL         found;

  TRACE_FUNCTION ("cmhMM_OperatorQuery()");
  
  if( mmShrdPrm.regStat EQ RS_FULL_SRV AND
      mmShrdPrm.usedPLMN.v_plmn EQ VLD_PLMN)
  {
    cmhMM_CnvrtPLMN2INT( mmShrdPrm.usedPLMN.mcc, mmShrdPrm.usedPLMN.mnc,
                         &mcc, &mnc );

    found = cmhMM_FindPLMN (&plmnDesc, mcc, mnc, mmShrdPrm.lac, FALSE);

    if (!found)
    {
      TRACE_EVENT("!cmhMM_FindPLMN()");
      if( format EQ COPS_FRMT_Numeric )
      {
         if ((mnc & 0x00F) EQ 0xF)
          /*lint -e{702} */
          sprintf (oper, "%03X%02X", mcc, mnc >> 4);
        else
          sprintf (oper, "%03X%03X", mcc, mnc);
      }
    }
    else
    {
      char longName[(MAX_ALPHA_OPER_LEN*8+6)/7];
      char shrtName[(MAX_ALPHA_OPER_LEN*8+6)/7];

      longName[0] = shrtName[0] = '\0';

      switch( format )
      {
        case( COPS_FRMT_Long ):
          TRACE_EVENT("COPS_FRMT_Long");

          if (plmnDesc.pnn)
          {
            if (plmnDesc.long_len)
            {
              switch (plmnDesc.long_ext_dcs>>4 & 0x07)
              {
                case 0x00:
                  utl_cvtPnn7To8((UBYTE *)plmnDesc.longName,
                                          plmnDesc.long_len,
                                          plmnDesc.long_ext_dcs,
                                 (UBYTE *)longName);
                  break;
                case 0x01:
                  TRACE_ERROR ("ERROR: Unhandled UCS2 DCS");
                  break;
                default:
                  TRACE_ERROR ("ERROR: Unknown DCS");
                  break;
              }
            }
            strncpy( oper, longName, MAX_ALPHA_OPER_LEN-1 );
          }
          else
          {
            strncpy( oper, plmnDesc.longName, MAX_ALPHA_OPER_LEN-1 );
          }
          oper[MAX_ALPHA_OPER_LEN-1] = '\0';
          break;

        case( COPS_FRMT_Short ):
          if (plmnDesc.pnn)
          {
            if (plmnDesc.shrt_len)
            {
              switch (plmnDesc.shrt_ext_dcs>>4 & 0x07)
              {
                case 0x00:
                  utl_cvtPnn7To8((UBYTE *)plmnDesc.shrtName,
                                          plmnDesc.shrt_len,
                                          plmnDesc.shrt_ext_dcs,
                                 (UBYTE *)shrtName);
                  break;
                case 0x01:
                  TRACE_ERROR ("ERROR: Unhandled UCS2");
                  break;
                default:
                  TRACE_ERROR ("ERROR: Unknown DCS");
                  break;
              }
            }
            strncpy( oper, shrtName, MAX_ALPHA_OPER_LEN-1 );
          }
          else
          {
            strncpy( oper, plmnDesc.shrtName, MAX_ALPHA_OPER_LEN-1 );
          }
          oper[MAX_ALPHA_OPER_LEN-1] = '\0';
          break;

      case( COPS_FRMT_Numeric ):
        if ((mnc & 0x00F) EQ 0xF)
          /*lint -e{702} */
          sprintf (oper, "%03X%02X", mcc, mnc >> 4);
        else
          sprintf (oper, "%03X%03X", mcc, mnc);
        break;
      }
    }
  }
}


/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)         MODULE  : CMH_MMF                  |
| STATE   : code                  ROUTINE : cmhMM_OperatorStoreInFFS      |
+--------------------------------------------------------------------+

  PURPOSE : 

  This function stores the Operator given by MCC and MNC, and the IMSI number in the FFS file
  given by MM_FFS_OPER_FILE. If the directory in which the file is stored does not exist, it will create it.
   
  
*/



GLOBAL BOOL cmhMM_OperatorStoreInFFS(UBYTE* mcc, UBYTE* mnc, UBYTE* IMSI)
{

#ifndef _SIMULATION_
  T_FFS_RET result;
#endif
  T_FFSPLMNIMSI currentPlmnIMSI;

  TRACE_FUNCTION("cmhMM_OperatorStoreInFFS");


  memcpy( currentPlmnIMSI.IMSI, IMSI, MAX_IMSI-1);
  memcpy(currentPlmnIMSI.mcc,mcc,SIZE_MCC);
  memcpy(currentPlmnIMSI.mnc,mnc,SIZE_MNC);

#ifndef _SIMULATION_

   switch(FFS_mkdir(MM_FFS_OPER_DIR))
  {/* Check / Create ffs directory  */
    case EFFS_OK:
      TRACE_EVENT("cmhMM_OperatorStoreInFFS: Dir  Created ");                        
      break;
    case EFFS_EXISTS:
      TRACE_EVENT("cmhMM_OperatorStoreInFFS:Dir  Exists ");                        
      break;
    default:
      TRACE_EVENT("cmhMM_OperatorStoreInFFS:Dir  Not Created");                  
      return ( FALSE );
  }

  result = FFS_fwrite(MM_FFS_OPER_FILE,  &currentPlmnIMSI, sizeof(T_FFSPLMNIMSI));
  TRACE_EVENT_P1("cmhMM_OperatorStoreInFFS:FFS dir ok,FFS_fwrite res %x", result);                  

  if (result < EFFS_OK)
    return FALSE;

#else /*Simulation is defined*/

  memcpy(&RAMPlmnIMSI,&currentPlmnIMSI,sizeof(T_FFSPLMNIMSI));

#endif

  return TRUE;

}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)         MODULE  : CMH_MMF                  |
| STATE   : code                  ROUTINE : cmhMM_OperatorReadFromFFS      |
+--------------------------------------------------------------------+

  PURPOSE : 

  This function reads the Operator given by MCC and MNC, and the IMSI number from the FFS file
  given by MM_FFS_OPER_FILE. 
   
  
*/


GLOBAL BOOL cmhMM_OperatorReadFromFFS(UBYTE* mcc, UBYTE* mnc, UBYTE* IMSI)
{
#ifndef _SIMULATION_
  T_FFS_RET result;
#endif

  T_FFSPLMNIMSI currentPlmnIMSI;

  TRACE_FUNCTION("cmhMM_OperatorReadFromFFS()");

#ifndef _SIMULATION_
  
  result = FFS_fread(MM_FFS_OPER_FILE,  &currentPlmnIMSI, sizeof(T_FFSPLMNIMSI));

  TRACE_EVENT_P1("FFS result = %d",result);

  if (result < EFFS_OK)
    return FALSE;

#else

  memcpy(&currentPlmnIMSI,&RAMPlmnIMSI,sizeof(T_FFSPLMNIMSI));


#endif

  memcpy( IMSI,currentPlmnIMSI.IMSI, MAX_IMSI-1);
  memcpy(mcc,currentPlmnIMSI.mcc,SIZE_MCC);
  memcpy(mnc,currentPlmnIMSI.mnc,SIZE_MNC);



  return TRUE;



}

 /*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)         MODULE  : CMH_MMF                  |
| STATE   : code                  ROUTINE : cmhMM_GetActingHPLMN     |
+--------------------------------------------------------------------+

  PURPOSE : fills the Acting HPLMN if present.
*/
GLOBAL BOOL cmhMM_GetActingHPLMN(SHORT * mccBuf, SHORT * mncBuf)
{
  TRACE_FUNCTION("cmhMM_GetActingHPLMN");
  if(mmShrdPrm.ActingHPLMN.v_plmn EQ VLD_PLMN)
  {
    cmhMM_CnvrtPLMN2INT( mmShrdPrm.ActingHPLMN.mcc, mmShrdPrm.ActingHPLMN.mnc, mccBuf, mncBuf );
    TRACE_EVENT("Acting HPLMN Present");
    return(TRUE);
  }
  TRACE_EVENT("Acting HPLMN not Present");
  return (FALSE);
}

/*
+-------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)    MODULE  : CMH_MMF                      |
| STATE   : code             ROUTINE : cmhMM_ONSReadName            |
+-------------------------------------------------------------------+

  PURPOSE : Sends a SIM read request

  RESULT: has an error occured ?
  Returns: FALSE if primitive has been sent and TRUE if not
*/
GLOBAL BOOL cmhMM_ONSReadName()
{
  TRACE_FUNCTION ("cmhMM_ONSReadName()");

  if (cmhSIM_ReadTranspEF (CMD_SRC_NONE,
                           AT_CMD_NONE,
                           SIM_CPHS_ONSTR,
                           0,
                           NOT_PRESENT_8BIT,
                           NULL,
                           cmhMM_ONSReadNameCb) EQ AT_FAIL)
  {
    TRACE_EVENT("FATAL ERROR");
    return(TRUE);
  }
  ONSReadStatus = ONS_READING;
  return(FALSE);
}

/*
+-------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)    MODULE  : CMH_MMF                      |
| STATE   : code             ROUTINE : cmhMM_ONSReadNameCb          |
+-------------------------------------------------------------------+

  PURPOSE : Call back for SIM retrieval of EF_ONS.
*/

GLOBAL void cmhMM_ONSReadNameCb(SHORT table_id)
{
  int i =0;

  TRACE_FUNCTION ("cmhMM_ONSReadNameCb()");

  if(!(simShrdPrm.atb[table_id].exchData NEQ NULL AND
                  simShrdPrm.atb[table_id].exchData[0] NEQ 0xFF AND
                  simShrdPrm.atb[table_id].errCode EQ SIM_NO_ERROR))
  {
    ONSReadStatus = ONS_READ_FAIL;
  }
  else
  {
    ONSReadStatus = ONS_READ_OVER;
    memset (&ONSDesc, 0, sizeof (T_OPER_ENTRY));
    ONSDesc.long_len = 0;
    while (simShrdPrm.atb[table_id].exchData[i] NEQ 0xFF AND
           ONSDesc.long_len < MAX_LONG_OPER_LEN - 1)
    {
      ONSDesc.long_len++;
      i++;
    }
    if (ONSDesc.long_len <= MAX_SHRT_OPER_LEN - 1)
    {
      ONSDesc.shrt_len = ONSDesc.long_len;
    }
    else
    {
      ONSDesc.shrt_len = MAX_SHRT_OPER_LEN - 1;
    }

    /* Issue OMAPS00058768 : EFons may have fewer bytes than MAX_LONG_OPER_LEN and if all of them are used bytes then 
       we will not come across 0xFF (unused bytes), which causes ONSDesc.long_len = MAX_LONG_OPER_LEN - 1. 
       So this leads to the function memcpy() to copy garbage characters in front of operator name.
    */
    ONSDesc.long_len = MINIMUM(simShrdPrm.atb[table_id].dataLen, ONSDesc.long_len);
    ONSDesc.shrt_len = MINIMUM(simShrdPrm.atb[table_id].dataLen, ONSDesc.shrt_len);

    memcpy (ONSDesc.longName, simShrdPrm.atb[table_id].exchData, ONSDesc.long_len);
    memcpy (ONSDesc.shrtName, simShrdPrm.atb[table_id].exchData, ONSDesc.shrt_len);

    ONSDesc.long_ext_dcs = ONS_EXT_DCS;
    ONSDesc.shrt_ext_dcs = ONS_EXT_DCS;

/* Issue OMAPS00058768 : For file update of ONS we no need to call cmhMM_Registered() */
#ifdef SIM_TOOLKIT
    if (simShrdPrm.fuRef < 0)
#endif
    {
      cmhMM_Registered ();
    }
#ifdef SIM_TOOLKIT
    if (simShrdPrm.fuRef >= 0)
    {
      psaSAT_FUConfirm (simShrdPrm.fuRef,
               (USHORT)((simShrdPrm.atb[table_id].errCode EQ SIM_NO_ERROR)?
               SIM_FU_SUCCESS: SIM_FU_ERROR));
    }
#endif
  }
    simShrdPrm.atb[table_id].ntryUsdFlg = FALSE;
}

/*
+-------------------------------------------------------------- +
| PROJECT : GSM-PS (6147)    MODULE  : CMH_MMF                  |
| STATE   : code             ROUTINE : cmhMM_Reset_ONSDesc      |
+---------------------------------------------------------------+

  PURPOSE :Issue OMAPS00058768: Reset ONSDesc.
*/

GLOBAL void cmhMM_Reset_ONSDesc()
{
  memset(&ONSDesc,0x00,sizeof(ONSDesc));
  ONSReadStatus = ONS_READ_NOT_DONE;
}
/*==== EOF ========================================================*/