/*
+-----------------------------------------------------------------------------
|  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 ========================================================*/