/* 
+----------------------------------------------------------------------------- 
|  Project :  MMI-Framework (8417)
|  Modul   :  PHB
+----------------------------------------------------------------------------- 
|  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 modul contains the functions to establish the phone book.
+----------------------------------------------------------------------------- 
*/ 

#ifdef TI_PS_FFS_PHB

#include "aci_all.h"
#include "aci_cmh.h"
#include "aci_mem.h"

#include "phb_aci.h"

#ifdef _SIMULATION_
  /*
   * Unfortunately we have a double definition of some preprocessor definitions
   * within windows.h and within the protocol stack, so we undefine here.
   */
  #undef PARITY_NONE
  #undef PARITY_ODD
  #undef PARITY_EVEN
  #undef DRV_OK
  #include "ffs_pc_api.h"
  #undef PARITY_NONE
  #undef PARITY_ODD
  #undef PARITY_EVEN
  #undef DRV_OK
#else
  #include "ffs/ffs.h"
#endif

#ifdef SIM_TOOLKIT
#include "psa.h"
#include "psa_sim.h"
#include "psa_cc.h"
#include "psa_sat.h"
#endif /* #ifdef SIM_TOOLKIT */

#include "cmh.h"
#include "cmh_phb.h"

#ifdef DTI
#include "dti_conn_mng.h"
#endif
#include "cmh_sim.h"


/*
 * Constants and enumerations
 */
//#define NR_EF_ICI             20  /* LRN, LMN */
//#define SIZE_EF_ICI           (28+16)  /* alpha id 16 bytes, 31.102 clause 4.2.33 */

#define NR_EF_OCI             10  /* Arbitrary, LDN */
#define SIZE_EF_OCI           (27+16)  /* alpha id 16 bytes, 31.102 clause 4.2.34 */

#define NR_EF_EXT5            10  /* Arbitrary */
#define SIZE_EF_EXT5          13  /* 31.102 clause 4.2.37 */

#define NR_EF_LRN             10  /* Arbitrary, LRN */
#define SIZE_EF_LRN           SIZE_EF_OCI

#define NR_EF_LMN             10  /* Arbitrary, LMN */
#define SIZE_EF_LMN           SIZE_EF_OCI

#define NR_EF_EXT_LRN         10  /* Arbitrary */
#define SIZE_EF_EXT_LRN       SIZE_EF_EXT5

#define NR_EF_EXT_LMN         10  /* Arbitrary */
#define SIZE_EF_EXT_LMN       SIZE_EF_EXT5

#define PHB_MAX_QUEUE         4   /* Queued LDN entries */
#define SIM_MAX_RECORD_SIZE   256 /* Maximum size of a SIM record */

#define PHB_STATE_NULL        0   /* NULL state before reading phonebook    */
#define PHB_STATE_IDLE        1   /* IDLE state, phonebook has been read    */
#define PHB_STATE_VERIFY      2   /* Verify SIM (Initialization, SAT)       */
#define PHB_STATE_READ        3   /* Reading from SIM (Initialization, SAT) */
#define PHB_STATE_WRITE       4   /* Write/Delete single record to SIM      */
#define PHB_STATE_DELETE_BOOK 5   /* Delete a whole SIM phonebook           */

#define SET_PHB_STATE(n)  pba_data.state = n;
#define GET_PHB_STATE()   pba_data.state

typedef enum
{
  INVALID_EXT = -1,
  EXT1,             /* ADN, LDN phonebook Extention */
  EXT2,             /* FDN phonebook Extention      */
  EXT3,
  EXT4,
  EXT5,             /* Release 1999+ for EF_ICI, EF_OCI */
  EXT_LRN,          /* Extension for LRN */
  EXT_LMN,          /* Extension for LMN */
  MAX_PHB_EXT
} T_EXT_TYPE; 

typedef struct 
{
  USHORT        index;
  T_PHB_RECORD  entry;
} T_PHB_QUEUE;

/*
 * Type definitions
 */
typedef struct
{
  /* Fixed Dialling Numbers mode */
  T_PHB_FDN_MODE fdn_mode;

  /* Fixed Dialling Number class type */
  T_ACI_CLASS fdn_classtype;

  /* T_PHB_STATE of the phonebook */
  T_PHB_STAT phb_stat;
  
  /* Current state of the phonebook PHB_STATE_XXX */
  UBYTE state;

  /* SIM data */
  UBYTE *data;
  
  /* SIM service table */
  UBYTE sim_service_table[MAX_SRV_TBL];

  /* Database has to be recreated (IMSI changed or DB unclean) */
  BOOL db_recreate;

  /* Paused elementary file if reading extension record, otherwise 0 */
  USHORT paused_ef;

  /* Paused record number if reading extension record, otherwise 0 */
  UBYTE paused_no;

  /* Delete all: Book to delete, Read: Current book reading */
  T_PHB_TYPE current_book;
  
  /* Delete all: current record */
  UBYTE del_record;

  /* Book created */
  BOOL book_created[MAX_PHONEBOOK];

  /* Extension created */
  BOOL ext_created[MAX_PHB_EXT];

  /* We are reading the 1st ext record only to get the number of records */
  BOOL ext_dummy_read;

  /* Read flags for SIM phonebooks. Set to TRUE when reading has started */
  BOOL book_read[MAX_PHONEBOOK];

  /* Maximum number of phonebook records */
  UBYTE phb_record_max[MAX_PHONEBOOK];
  /* Maximum number of extension records */
  UBYTE ext_record_max[MAX_PHB_EXT];

  /* Record sizes of phonebook records */
  UBYTE phb_record_len[MAX_PHONEBOOK];
  /* Record sizes of extension records */
  UBYTE ext_record_len[MAX_PHB_EXT];

  /* Queued LDN entries during startup */
  UBYTE c_queued;
  T_PHB_QUEUE *queued[PHB_MAX_QUEUE];
  
  /* Records to be synchronized to the SIM */
  T_DB_CHANGED records_changed;
} T_PHB_ACI_DATA;

/* We pack all internal data into one data structure */
LOCAL T_PHB_ACI_DATA pba_data;


/* 
 * Prototypes for local functions
 */
LOCAL UBYTE pb_get_max_num_len          (T_PHB_TYPE type);
LOCAL BOOL pb_sim_service               (UBYTE nr);
LOCAL BOOL pb_read_sim_record           (USHORT data_id,
                                         UBYTE rcd_num,
                                         UBYTE len);
LOCAL BOOL pb_read_sim (USHORT data_id, UBYTE len, UBYTE max_length);
LOCAL void pb_read_next_sim_book        (void);
LOCAL void pb_read_sim_record_cb        (SHORT table_id);
LOCAL void pb_read_sim_cb               (SHORT table_id);
LOCAL void pb_sat_update_reset          (USHORT ef);
LOCAL T_PHB_RETURN pb_sync_next_sim_record (BOOL first);
LOCAL T_PHB_RETURN pb_write_sim_record  (USHORT ef,
                                         UBYTE phy_recno,
                                         UBYTE entry_size,
                                   const UBYTE *buffer);
LOCAL void pb_write_sim_record_cb       (SHORT table_id);
T_PHB_TYPE pb_get_phb_type_from_ef      (USHORT ef);
T_EXT_TYPE pb_get_ext_type_from_ef      (USHORT ef);
LOCAL void pb_status_ind                (T_PHB_STAT phb_stat,
                                         SHORT cmeError);
LOCAL T_PHB_RETURN pb_write_queue       (const T_PHB_RECORD *entry);
LOCAL void pb_clear_queue               (void);
LOCAL T_PHB_RETURN pb_read_queue        (void);

/*
 * Functions - Interface functions to external modules
 */


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

  PURPOSE : Power-on initialization of the phonebook module. This function
            is called at a point in time by pei_init() when it cannot be 
            expected that the protocol stack has been booted up completely,
            so it is undesirable to do anything here which might rely on
            a module still not started up like the PCM or the FFS.
  
*/
GLOBAL void pb_init (void)
{
  TRACE_FUNCTION ("pb_init()");

  memset (&pba_data, 0, sizeof (T_PHB_ACI_DATA));

  pba_data.fdn_mode = NO_OPERATION;
  pba_data.fdn_classtype = CLASS_VceDatFaxSms;

  pb_sim_init();

#ifdef SIM_TOOLKIT
  simShrdPrm.fuRef= - 1;
  psaSAT_FURegister (pb_update);
#endif /* #ifdef SIM_TOOLKIT */

  SET_PHB_STATE (PHB_STATE_NULL);
}


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

  PURPOSE : This function synchronizes, if necessary, all unwritten data,
            shuts down the SIM phonebook layer and deallocates all 
            resources. The SIM itself is not deactivated here.
            

*/
LOCAL void pb_exit_phb (void)
{
  TRACE_FUNCTION ("pb_exit_phb()");

  /* 
   * Flush everything which has not yet been written.
   * Note: It's a good idea if everything is written immediately
   * so that we don't need to flush anything here. Competitor phones
   * do the job in this way, this has the advantage that even after
   * a phone crash immediately after dialling a number or missing
   * a call nothing is lost.
   */
  (void)pb_sim_flush_data ();

  /* Shutdown the lower layers */
  pb_sim_exit ();

  /*
   * Free all allocated resources
   */
  if (pba_data.data NEQ NULL)
  {
    ACI_MFREE (pba_data.data);
    pba_data.data = NULL;
  }

  pb_clear_queue ();

  /*
   * Set phonebook to no operation 
   */
  pba_data.fdn_mode = NO_OPERATION;
  pba_data.phb_stat = PHB_UNKNOWN;  
  SET_PHB_STATE (PHB_STATE_NULL);
}


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

  PURPOSE : Shutdown phonebook and the SIM itself also.

*/
GLOBAL void pb_exit (void)
{
  TRACE_FUNCTION ("pb_exit()");

  /* Shutdown SIM phonebook */
  pb_exit_phb ();

  /* Shutdown the SIM itself */
  simShrdPrm.synCs = SYNC_DEACTIVATE;
  psaSIM_SyncSIM();
}


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

  PURPOSE : Resets the phonebook, basically pb_exit() / pb_init().
  
*/
GLOBAL void pb_reset (void)
{
  TRACE_FUNCTION ("pb_reset()");

  if (GET_PHB_STATE() EQ PHB_STATE_NULL)
    return;

  pb_exit_phb();
  pb_init();
}

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

  PURPOSE : This function set's the emergency call numbers.
            This can only happen at startup time when the SIM when
            a SIM_ACTIVATE_CNF / SIM_ACTIVATE_IND is received.
            At this point in time the entering of PIN may be outstanding,
            the only information we may have gotten are the emergency call
            numbers.

*/
GLOBAL void pb_set_sim_ecc (USHORT cause,
                            UBYTE ecc_len,
                            const UBYTE *sim_ecc)
{
  TRACE_FUNCTION ("pb_set_sim_ecc()");

  if (pba_data.fdn_mode NEQ NO_OPERATION)
  {
     /* 
      * Setting of the emergency call numbers after having gotten the
      * IMSI of the SIM is ignored.
      */
    return;
  }

  if ((cause EQ SIM_CAUSE_OTHER_ERROR) OR
      (cause EQ SIM_CAUSE_CARD_REMOVED) OR
      (cause >= SIM_CAUSE_PARAM_WRONG AND cause <= SIM_CAUSE_DRV_TEMPFAIL))
  {
    /* 
     * Some error from the SIM. Indicate a fallback to the PCM/FFS.
     */
    (void)pb_sim_set_ecc (0, NULL);
  }
  else
  {
    (void)pb_sim_set_ecc (ecc_len, sim_ecc);
  }



}


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

  PURPOSE : This function is called when the PIN has been entered and we have
            the IMSI and full access on the SIM phonebook. Reading/Verifying
            of phonebook data is not started at this point except for the 
            ECC phonebook.

*/
GLOBAL void pb_inserted_sim (UBYTE c_sim_serv,
                             UBYTE *sim_serv,
                             const T_imsi_field *imsi_field,
                             UBYTE adn_bdn_fdn_func,
                             UBYTE phase)       // For further study
{
  T_FFS_SIZE ffs_size;                /* FFS result code */
  T_ACI_CLASS classFDN;               /* FDN class */
  UBYTE ub_class = (UBYTE)CLASS_None; /* T_ACI_CLASS */

  TRACE_FUNCTION ("pb_inserted_sim()");

  /* Store SIM service table */
  if (c_sim_serv > MAX_SRV_TBL)
    c_sim_serv = MAX_SRV_TBL; /* Garbage protection */
  memcpy (pba_data.sim_service_table, sim_serv, c_sim_serv);

  /* 
   * Open SIM phonebook. Remember whether the database has to be recreated 
   * for some reason (SIM changed, database unclean).
   */
  if (pb_sim_open (imsi_field, &pba_data.db_recreate) NEQ PHB_OK)
  {
    /* We're here in really big trouble and can do nothing about it */
    TRACE_ERROR ("Fatal: pb_sim_open() NEQ PHB_OK");
  }

  /*
   * Update ECC phonebook
   */
  pba_data.book_read[ECC] = TRUE;
  if (!pb_read_sim (SIM_ECC, NOT_PRESENT_8BIT, (UBYTE)SIM_MAX_RECORD_SIZE))
  {
    /* Unexpected problem. Live with previously read values */
    TRACE_ERROR ("Unexpected: No free SIM slot");
  }

  switch (adn_bdn_fdn_func)
  {
    case SIM_ADN_ENABLED:
    case SIM_ADN_BDN_ENABLED:
      pba_data.fdn_mode = FDN_DISABLE;
      break;

    case SIM_FDN_ENABLED:
    case SIM_FDN_BDN_ENABLED:
      pba_data.fdn_mode = FDN_ENABLE;

      /* read last fdn_classtype from FFS */
      ffs_size = ffs_file_read ("/mmi/fdnClassType", &ub_class, sizeof(ub_class));
      
      if (ffs_size EQ sizeof(ub_class)) /* successful read */
      {
        classFDN = (T_ACI_CLASS)ub_class;
        
        /* only these two classes are currently supported */
        if (classFDN EQ CLASS_VceDatFax OR 
            classFDN EQ CLASS_VceDatFaxSms)
        {
          pba_data.fdn_classtype = classFDN;
        }
      }
      break;

    default: /* SIM_NO_OPERATION or trash */
      pba_data.fdn_mode = NO_OPERATION;
      break;
  }
}


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

  PURPOSE : This function is called when the flash phonebook has to be 
            read/verified from/against the phonebook stored on the SIM card.
            This is triggered by the reception of the 
            MNSMS_REPORT_IND (SMS_STATE_READY) primitive.

*/
GLOBAL void pb_start_build (BOOL unchanged)
{
  TRACE_FUNCTION ("pb_start_build()");
  
  if (pba_data.db_recreate)
  {
    /* 
     * Indicate a busy phonebook. We have to re-read all from scratch 
     */
    pb_status_ind (PHB_BUSY, CME_ERR_NotPresent);
    SET_PHB_STATE (PHB_STATE_READ);
  }
  else
  {
    /* 
     * SIM (IMSI) not changed and database clean,
     * allow reading from the SIM phonebook by the MMI.
     */
    pb_status_ind (PHB_READY, CME_ERR_NotPresent);
    SET_PHB_STATE (PHB_STATE_VERIFY);
  }

  pba_data.ext_dummy_read = FALSE;

#ifdef _SIMULATION_
  TRACE_EVENT_P1 ("SRV_ADN: %s",  pb_sim_service (SRV_ADN)  ? "YES" : "NO");
  TRACE_EVENT_P1 ("SRV_FDN: %s",  pb_sim_service (SRV_FDN)  ? "YES" : "NO");
  TRACE_EVENT_P1 ("SRV_CCP: %s",  pb_sim_service (SRV_CCP)  ? "YES" : "NO");
  TRACE_EVENT_P1 ("SRV_MSISDN: %s",pb_sim_service (SRV_MSISDN) ? "YES" : "NO");
  TRACE_EVENT_P1 ("SRV_EXT1: %s", pb_sim_service (SRV_EXT1) ? "YES" : "NO");
  TRACE_EVENT_P1 ("SRV_EXT2: %s", pb_sim_service (SRV_EXT2) ? "YES" : "NO");
  TRACE_EVENT_P1 ("SRV_LDN: %s",  pb_sim_service (SRV_LDN)  ? "YES" : "NO");
  TRACE_EVENT_P1 ("SRV_SDN: %s",  pb_sim_service (SRV_SDN)  ? "YES" : "NO");
  TRACE_EVENT_P1 ("SRV_EXT3: %s", pb_sim_service (SRV_EXT3) ? "YES" : "NO");
  TRACE_EVENT_P1 ("SRV_BDN: %s",  pb_sim_service (SRV_BDN)  ? "YES" : "NO");
  TRACE_EVENT_P1 ("SRV_EXT4: %s", pb_sim_service (SRV_EXT4) ? "YES" : "NO");
#endif

  pb_read_next_sim_book();
}


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

  PURPOSE : This function is called when the SIM wants to inform the 
            phonebook that some elementary files have been changed.
            Returns TRUE if no files aere changed affecting the phonebook,
            otherwise FALSE.

*/
GLOBAL BOOL pb_update (int ref,
                       T_SIM_FILE_UPDATE_IND *fu)
{
  unsigned i;           /* Index variable */
  T_PHB_TYPE phb_type;  /* Phonebook type */ 
  T_EXT_TYPE ext_type;  /* Extension type */ 
  USHORT ef;            /* Elementary file number */

  TRACE_FUNCTION ("pb_update()");

  simShrdPrm.fuRef = -1;  /* Assume phonebook is not affected */

  for (i = 0; i < fu->val_nr; i++)
  {
    ef = fu->file_id[i];

    if (ef EQ SIM_SST)
    {
      /*
       * When SIM service table is changed, then all SIM phonebooks
       * will be updated.
       */

      /* Mark all phonebooks as unread */
      pb_sat_update_reset (SIM_ECC);
      pb_sat_update_reset (SIM_ADN);
      pb_sat_update_reset (SIM_FDN);
      pb_sat_update_reset (SIM_BDN);
#ifdef SIM_LND_SUPPORT
      pb_sat_update_reset (SIM_LND);
#endif
      pb_sat_update_reset (SIM_MSISDN);
      pb_sat_update_reset (SIM_SDN);

      /* Mark all extensions as unread */
      pb_sat_update_reset (EXT1);
      pb_sat_update_reset (EXT2);
      pb_sat_update_reset (EXT3);
      pb_sat_update_reset (EXT4);

      if (!pb_read_sim (SIM_SST, NOT_PRESENT_8BIT, (UBYTE)SIM_MAX_RECORD_SIZE))
      {
        /*
         * We could not get an empty slot and are in trouble now as there is
         * no good error handling possible here. On the other hand, it is 
         * not expected ever that an error occurs here.
         */
        TRACE_ERROR ("Internal problem getting a SIM slot");
        return TRUE;  /* No update in progress */
      }

      simShrdPrm.fuRef = ref;   /* Something to do */

      SET_PHB_STATE (PHB_STATE_READ);
      return FALSE; /* Update in progress */
    }
  }

  /*
   * At least the SIM service table has not been changed, check now for
   * single phonebooks and extension records.
   */
  
  for (i = 0; i < fu->val_nr; i++)
  {
    ef = fu->file_id[i];
    phb_type = pb_get_phb_type_from_ef (ef);
    ext_type = pb_get_ext_type_from_ef (ef);

    if (phb_type NEQ INVALID_PHB)
    {
      /* Phonebook affected by change */
      simShrdPrm.fuRef = ref;

      /* Reset respective field */
      pb_sat_update_reset (ef);

      switch (phb_type)
      {
        case ADN:
          pb_sat_update_reset (SIM_FDN);
          break;

        case FDN:
          pb_sat_update_reset (SIM_ADN);
          break;
        
        default:
          break;
      }
    } 
    else if (ext_type NEQ INVALID_EXT)
    {
      /* Phonebook affected by change */
      simShrdPrm.fuRef = ref;

      /* Reset respective field */
      pb_sat_update_reset (ef);

      /* Mark also the appropriate book itself as unread */
      switch (ext_type)
      {
        case EXT1:
          pb_sat_update_reset (SIM_ADN);
          pb_sat_update_reset (SIM_MSISDN);
#ifdef SIM_LND_SUPPORT
          pb_sat_update_reset (SIM_LND);
#endif
          break;

        case EXT2:
          pb_sat_update_reset (SIM_FDN);
          break;

        case EXT3:
          pb_sat_update_reset (SIM_SDN);
          break;

        case EXT4:
          pb_sat_update_reset (SIM_EXT4);
          break;

        default:
          TRACE_ERROR ("Unexpected default"); /* Everything caught */
          break;
      }
    }
  }

  if (simShrdPrm.fuRef NEQ -1)
  {
    pb_start_build (FALSE);
    return FALSE; /* FALSE means update in progress */
  }

  return TRUE; /* TRUE means nothing to do */
}


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

  PURPOSE : This function is called when someone wants to flush some pending
            data to persistent memory. Probably a bad concept, it's always
            better to flush always internally if some operation is finished
            and to synchronize also to the SIM immediately. This has the
            advantage that even after a crash nothing is lost.

*/
T_PHB_RETURN pb_flush_data (void)
{
  TRACE_FUNCTION ("pb_flush_data()");

  /* This function is empty, and this should remain this way if possible */
  return PHB_OK;
}


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

  PURPOSE : This function adds or replaces a phonebook entry.

*/
GLOBAL T_PHB_RETURN pb_add_record (T_PHB_TYPE type,
                                   USHORT phy_recno,
                                   const T_PHB_RECORD *entry)
{
  T_PHB_RETURN phb_result;
  T_DB_CHANGED dummy_changed;

  TRACE_FUNCTION ("pb_add_record()");

#ifdef _SIMULATION_
  {
    char name [PHB_MAX_TAG_LEN + 1];
    char number[MAX_PHB_NUM_LEN];

    memset (name, 0, PHB_MAX_TAG_LEN + 1);
    memcpy (name, entry->tag, entry->tag_len);
    cmhPHB_getAdrStr (number,
                      MAX_PHB_NUM_LEN - 1,
                      entry->number,
                      entry->len);
    if ((UBYTE)(name[0]) >= 0x80)
      strcpy (name, "<Some UCS2 coding>");

    TRACE_EVENT_P4 ("Adding number %s with name '%s' to book %d record %d",
                    number, name, type, phy_recno);
  }
#endif

  /* Fix phy_recno parameter for circular books */
  if ((type EQ LDN) OR (type EQ LMN) OR (type EQ LRN))
  {
    if (phy_recno EQ 0)
      phy_recno = 1;
  }

  switch (GET_PHB_STATE())
  {
    case PHB_STATE_IDLE:

      /* Add the data in the database */
      phb_result = pb_sim_add_record (type, 
                                      phy_recno, 
                                      entry, 
                                      &pba_data.records_changed);
      if (phb_result NEQ PHB_OK)
        return phb_result;  /* Record could not be added to the database */

      if (pba_data.records_changed.entries NEQ 0)
      {
        SET_PHB_STATE (PHB_STATE_WRITE);
      }

      /* Start synchronizing record(s) to the SIM */
      return pb_sync_next_sim_record (TRUE);

    case PHB_STATE_VERIFY:
    case PHB_STATE_READ:
      if ((type EQ LRN) OR (type EQ LMN))
      {
        /* 
         * In Release 1998- there is no counterpart on the SIM here,
         * we can write immediately as we don't have to synch to the SIM.
         */
        return (pb_sim_add_record (type, 
                                   phy_recno, 
                                   entry, 
                                   &dummy_changed));
      }
      else if (type EQ LDN)
      {
        /* We have to queue the LDN entry and will write later */
        return pb_write_queue (entry);
      }
      return PHB_FAIL;

    default:
      return PHB_FAIL;
  }
}


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

  PURPOSE : This function deletes a phonebook entry.

*/
GLOBAL T_PHB_RETURN pb_del_record (T_PHB_TYPE type,
                                   USHORT phy_recno)
{
  T_PHB_RETURN phb_result;

  TRACE_FUNCTION ("pb_del_record()");

  if (GET_PHB_STATE() NEQ PHB_STATE_IDLE)
    return PHB_FAIL;

  /* Delete the data in the database */
  phb_result = pb_sim_del_record (type,
                                  phy_recno,
                                  &pba_data.records_changed);
  if (phb_result NEQ PHB_OK)
    return phb_result;  /* Record could not be deleted from the database */

  if (pba_data.records_changed.entries NEQ 0)
  {
    SET_PHB_STATE (PHB_STATE_WRITE);
  }

  /* Start synchronizing record(s) to the SIM */
  return pb_sync_next_sim_record (TRUE);
}


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

  PURPOSE : This function checks whether a phone number is in FDN phonebook.

*/
#define RETURN(x) { retVal = x; goto cleanup_exit; }
/*lint -e{801} Use of goto*/
GLOBAL T_PHB_RETURN pb_check_fdn (UBYTE toa,
                                  const UBYTE *number)
{
  T_PHB_RETURN retVal;
  T_PHB_RECORD *phb_record;
  size_t cmp_len;
  CHAR cur_number[MAX_PHB_NUM_LEN];
  USHORT phy_recno;
  SHORT max_rcd;
  SHORT dummy_used_rcd;
  UBYTE dummy_tag_len;

  ACI_MALLOC (phb_record, sizeof (T_PHB_RECORD));

  TRACE_FUNCTION ("pb_check_fdn()");

  retVal = pb_sim_read_sizes (FDN, 
                              &max_rcd, 
                              &dummy_used_rcd, 
                              &dummy_tag_len);
  if (retVal NEQ PHB_OK)
  {
    TRACE_ERROR ("Unexpected return from pb_sim_read_sizes()");
    RETURN (retVal)
  }
  
  /* 
   * Do a linear search through all the records applying the number comparision
   * rules which make us pass 51.010 test case 27.18.1.1.4.2.
   * Those rules are different from those which are to be applied if a matching
   * record has to be found for an incoming call, therefore we cannot use 
   * pb_sim_search_number() here.
   * As normally FDN phonebooks have not more than 10 entries (max_rcd) a 
   * simple linear search will fulfill here all timing requirements.
   */
  
  for (phy_recno = 1; phy_recno <= max_rcd; phy_recno++)
  {
    retVal = pb_sim_read_record (FDN, phy_recno, phb_record);
    if (retVal EQ PHB_OK)
    {
      cmhPHB_getAdrStr (cur_number,
                        MAX_PHB_NUM_LEN - 1,
                        phb_record->number,
                        phb_record->len);
      cmp_len = strlen (cur_number);
      if (strncmp ((char *)number, cur_number, cmp_len) EQ 0)
      {
        /* Number matches. ACI-SPR-11927: check type of address */ 
        if ((toa EQ 0) OR
            (toa EQ phb_record->ton_npi) OR
            (phb_record->ton_npi EQ 0xFF))
        {
          RETURN (PHB_OK)
        }
      }
    }
  }
  RETURN (PHB_FAIL) /* Not found */

cleanup_exit:
  ACI_MFREE (phb_record);
  return retVal;
}


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

  PURPOSE : This function switches FDN and ADN phonebooks (sAT_PlusCLCK()).

*/
GLOBAL T_PHB_RETURN pb_switch_adn_fdn (T_PHB_FDN_MODE mode,
                                       T_ACI_CLASS classFDN)
{
  TRACE_FUNCTION ("pb_switch_adn_fdn()");

  if (GET_PHB_STATE() NEQ PHB_STATE_IDLE)
    return PHB_FAIL;

  /* Check parameters */
  switch (mode)
  {
    case FDN_DISABLE:
    case FDN_ENABLE:
      break;

    default:
      return PHB_FAIL;
  }

  if (classFDN NEQ pba_data.fdn_classtype)
  {
    UBYTE ub_class;
    
    ub_class = (UBYTE)classFDN;
    pba_data.fdn_classtype = classFDN;

#ifndef _SIMULATION_
    if (ffs_fwrite("/mmi/fdnClassType", &classFDN, sizeof(ub_class)) < 1)
    {
      TRACE_ERROR ("sAT_PlusCLCK: failed to write /mmi/fdnClassType");
    }
#endif /* #ifndef _SIMULATION_ */
  }

#if 1

  pba_data.fdn_mode = mode;

  /* Remove ADN from database */
  (void)pb_sim_remove_ef (SIM_ADN);
  pba_data.book_created[ADN] = FALSE;
  pba_data.book_read[ADN] = FALSE;

  /* Remove FDN from database */
  (void)pb_sim_remove_ef (SIM_FDN);
  pba_data.book_created[FDN] = FALSE;
  pba_data.book_read[FDN] = FALSE;

  /* Remove EXT2 from database */
  (void)pb_sim_remove_ef (SIM_EXT2);
  pba_data.ext_created[EXT2] = FALSE;

  /* Force cmhPHB_StatIndication (PHB_BUSY, CME_ERR_NotPresent) */
  pba_data.db_recreate = TRUE;

  SET_PHB_STATE (PHB_STATE_READ);

  /* Start reading changed books */
  pb_start_build (FALSE);
#else
  // This optimization does not comply with current test cases,
  // not understood perfectly whether the test cases can be
  // changed, so kept old behaviour.
  if (pba_data.fdn_mode NEQ mode)
  {
    pba_data.fdn_mode = mode;

    if (mode EQ FDN_ENABLE)
    {
      /* Remove ADN from database */
      (void)pb_sim_remove_ef (SIM_ADN);
      pba_data.book_created[ADN] = FALSE;
      pba_data.book_read[ADN] = FALSE;
    }
    else
    {
      /* Remove FDN from database */
      (void)pb_sim_remove_ef (SIM_FDN);
      pba_data.book_created[FDN] = FALSE;
      pba_data.book_read[FDN] = FALSE;

      /* Remove EXT2 from database */
      (void)pb_sim_remove_ef (SIM_EXT2);
      pba_data.ext_created[EXT2] = FALSE;
    }

    /* Force cmhPHB_StatIndication (PHB_BUSY, CME_ERR_NotPresent) */
    pba_data.db_recreate = TRUE;

    SET_PHB_STATE (PHB_STATE_READ);

    /* Start reading changed books */
    pb_start_build (FALSE);
  }
#endif
  return PHB_OK;
}


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

  PURPOSE : This function deletes a whole SIM phonebook

*/
GLOBAL T_PHB_RETURN pb_del_book (T_PHB_TYPE book)
{
  T_PHB_RETURN phb_result;
  UBYTE max_record;

  TRACE_FUNCTION ("pb_del_book()");

  if (GET_PHB_STATE() NEQ PHB_STATE_IDLE)
    return PHB_FAIL;

  /* Remember book to delete, initialize record to delete */
  pba_data.current_book = book;
  pba_data.del_record = 1;
  max_record = pba_data.phb_record_max[pba_data.current_book];

  do
  {
    /* Delete the record in the database */
    phb_result = pb_sim_del_record (pba_data.current_book, 
                                    pba_data.del_record,
                                    &pba_data.records_changed);
    if (phb_result NEQ PHB_OK)
      return phb_result;
    pba_data.del_record++;
  } 
  while ((pba_data.del_record <= max_record) AND
         (pba_data.records_changed.entries EQ 0));

  if (pba_data.records_changed.entries NEQ 0)
  {
    /* Synchronize to SIM */
    SET_PHB_STATE (PHB_STATE_DELETE_BOOK);
  
    /* Start synchronizing record(s) to the SIM */
    return pb_sync_next_sim_record (TRUE);
  }

  return PHB_OK;
}


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

  PURPOSE : This function gets the FDN class type

*/
GLOBAL T_ACI_CLASS pb_get_fdn_classtype (void)
{
  TRACE_FUNCTION ("pb_get_fdn_classtype()");

  return pba_data.fdn_classtype;

}


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

  PURPOSE : This function gets the FDN mode

*/
GLOBAL T_PHB_FDN_MODE pb_get_fdn_mode (void)
{
  TRACE_FUNCTION ("pb_get_fdn_mode()");

  return pba_data.fdn_mode;

}


/*
 * Wrapper functions. Wrapper functions don't define own functionality 
 * except of routing to a different block which is the SIM part of 
 * the phonebook here. For a description of these functions have
 * a look into the SIM part of the phonebook.
 */

GLOBAL T_PHB_RETURN pb_read_record (T_PHB_TYPE type,
                                    SHORT phy_recno,
                                    T_PHB_RECORD *entry)
{
  T_PHB_RETURN phb_result;

  TRACE_FUNCTION ("pb_read_record()");

  if (phy_recno EQ 0)
    return PHB_FAIL;

  switch (GET_PHB_STATE())
  {
    case PHB_STATE_VERIFY:
      /* 
       * For LDN records, first have a look into the queue
       */
      if ((type EQ LDN) AND (phy_recno - 1 < pba_data.c_queued))
      {
        /* Entry freshly dialled and still in the queue */
        *entry = pba_data.queued[phy_recno - 1]->entry;
        return PHB_OK;
      }
      phb_result = pb_sim_read_record (type, 
                                       (USHORT)(phy_recno - pba_data.c_queued),
                                       entry);
      TRACE_EVENT_P3 ("type = %d, phy_recno = %d, phb_result = %d",
                      type, phy_recno, phb_result);
      return phb_result;

    case PHB_STATE_READ:
      /* 
       * For LDN records, first have a look into the queue
       */
      if ((type EQ LDN) AND (phy_recno - 1 < pba_data.c_queued))
      {
        /* Entry freshly dialled and still in the queue */
        *entry = pba_data.queued[phy_recno - 1]->entry;
        return PHB_OK;
      }
      return PHB_FAIL; /* We already know the SIM was changed */

    case PHB_STATE_IDLE:
      phb_result = pb_sim_read_record (type, phy_recno, entry);
      TRACE_EVENT_P3 ("type = %d, phy_recno = %d, phb_result = %d",
                      type, phy_recno, phb_result);
      return phb_result;

    default:
      return PHB_FAIL;
  }
}


GLOBAL T_PHB_RETURN pb_read_alpha_record (T_PHB_TYPE type,
                                          SHORT order_num,
                                          T_PHB_RECORD *entry)
{
  TRACE_FUNCTION ("pb_read_alpha_record()");

  switch (GET_PHB_STATE())
  {
    case PHB_STATE_IDLE:
    case PHB_STATE_VERIFY:
      return pb_sim_read_alpha_record (type, order_num, entry);
    default:
      return PHB_FAIL;
  }
}


GLOBAL T_PHB_RETURN pb_read_number_record (T_PHB_TYPE type,
                                           SHORT order_num,
                                           T_PHB_RECORD *entry)
{
  TRACE_FUNCTION ("pb_read_number_record()");

  switch (GET_PHB_STATE())
  {
    case PHB_STATE_IDLE:
    case PHB_STATE_VERIFY:
      return pb_sim_read_number_record (type, order_num, entry);
    default:
      return PHB_FAIL;
  }
}


GLOBAL T_PHB_RETURN pb_search_name (T_PHB_TYPE type,
                                    T_PHB_MATCH match,
                                    const T_ACI_PB_TEXT *search_tag,
                                    SHORT *order_num)
{
  T_PHB_RETURN phb_result;

  TRACE_FUNCTION ("pb_search_name()");

  switch (GET_PHB_STATE())
  {
    case PHB_STATE_IDLE:
    case PHB_STATE_VERIFY:
      TRACE_EVENT_P1 ("order_num = %d", *order_num);
      phb_result = pb_sim_search_name (type, match, search_tag, order_num);
      if (phb_result EQ PHB_OK)         // #HM# Remove before production
      {
        TRACE_EVENT ("Entry found");
      }
      else
      {
        TRACE_EVENT ("Entry not found");
      }
      return phb_result;

    default:
      return PHB_FAIL;
  }
}


GLOBAL T_PHB_RETURN pb_search_number (T_PHB_TYPE type,
                                      const UBYTE *number,
                                      SHORT *order_num)
{
  TRACE_FUNCTION ("pb_search_number()");

  switch (GET_PHB_STATE())
  {
    case PHB_STATE_IDLE:
    case PHB_STATE_VERIFY:
      return pb_sim_search_number (type, number, order_num);
    default:
      return PHB_FAIL;
  }
}


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

  PURPOSE : This function gets the maximum length of a number within a
            given phonebook. The length depends on the given phonebook and
            the availability of extension records (SIM service table).
            
            Question: From an architectural point of view, belongs this into
            the ACI part of the phonebook or more into the SIM part of the 
            phonebook? SIM part until now has no information about SIM service
            table.

*/
LOCAL UBYTE pb_get_max_num_len (T_PHB_TYPE type)
{
  TRACE_FUNCTION ("pb_get_max_num_len()");

  switch (type)
  {
    case ECC:
      return 3;

    case ADN:
    case UPN:
    case ADN_ME:
      if (pb_sim_service (SRV_EXT1))
        return 2 * PHB_PACKED_NUM_LEN;
      break;

    case FDN:
      if (pb_sim_service (SRV_EXT2))
        return 2 * PHB_PACKED_NUM_LEN;
      break;

    case SDN:
      if (pb_sim_service (SRV_EXT3))
        return 2 * PHB_PACKED_NUM_LEN;
      break;
    
    case BDN:
      if (pb_sim_service (SRV_EXT4))
        return 2 * PHB_PACKED_NUM_LEN;
      break;

    case LDN:
    case LRN:
    case LMN:
    case ME:
      return 2 * PHB_PACKED_NUM_LEN;
    
    default:
      TRACE_ERROR ("Invalid phonebook type");
      return 0;
  }
  return 20; /* Default according to 11.11 if no EXT records and not ECC */
}


GLOBAL T_PHB_RETURN pb_read_sizes (T_PHB_TYPE type, /* IN */
                                   SHORT *max_rcd,  /* OUT */
                                   SHORT *used_rcd, /* OUT */
                                   UBYTE *num_len,  /* OUT */
                                   UBYTE *tag_len)  /* OUT */
{
  T_PHB_RETURN phb_result;

  TRACE_FUNCTION ("pb_read_sizes()");

  switch (GET_PHB_STATE())
  {
    case PHB_STATE_IDLE:
    case PHB_STATE_VERIFY:

      /* 
       * For a valid phonebook type, set all parameters to zero instead of 
       * reporting an error.
       */
      switch (type)
      {
        case ECC:
        case ADN:
        case FDN:
        case BDN:
        case LDN:
        case LRN:
        case SDN:
        case LMN:
        case UPN:
      /*case ME:
        case ADN_ME:*/
          *num_len = pb_get_max_num_len (type);
          phb_result = pb_sim_read_sizes (type, max_rcd, used_rcd, tag_len);
          if (phb_result NEQ PHB_OK)
          {
            *max_rcd  = 0;
            *used_rcd = 0;
            *num_len  = 0;
            *tag_len  = 0;
          }
          return PHB_OK;

        default: 
          break;
      }
      return PHB_FAIL;  /* Invalid phonebook */

    default:
      return PHB_FAIL;  /* Invalid state */
  }
}


GLOBAL int pb_get_entry_len (const UBYTE *pb_tag,
                             UBYTE max_pb_len)
{
  TRACE_FUNCTION ("pb_get_entry_len()");

  return pb_sim_get_entry_len (pb_tag, max_pb_len);
}


GLOBAL int pb_find_free_record (T_PHB_TYPE type)
{
  /* 
   * This function finds the number of the first free record in 
   * a given phonebook. This is new functionality as from S621.
   */
  TRACE_FUNCTION ("pb_find_free_record()");

  switch (GET_PHB_STATE())
  {
    case PHB_STATE_IDLE:
    case PHB_STATE_VERIFY:
      return pb_sim_find_free_record (type);
    default:
      return PHB_FAIL;
  }
}


/* 
 * Internal functions, not part of the interface
 */



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

  PURPOSE : This function checks whether a given service is
            "allocated and activated" on the SIM, if so TRUE is returned, 
            otherwise the function returns FALSE.

            No, using psaSIM_ChkSIMSrvSup() is not a good alternative here
            as we may have to handle the situation where the SIM service 
            table itself has been changed, in this case we are not signalled
            by the ACI when we have again a valid SIM service table, therefore
            this function exist here within the phonebook.

*/
LOCAL BOOL pb_sim_service (UBYTE nr)
{
  UBYTE byte_offset;
  UBYTE bit_offset;
  UBYTE service;

  TRACE_FUNCTION ("pb_sim_service()");

  if (nr > MAX_SRV_TBL * 4)
    return FALSE; /* Table overflow */

  byte_offset =  (nr - 1) / 4;
  bit_offset  = ((nr - 1) & 0x03) * 2;
  service = (pba_data.sim_service_table[byte_offset] >> bit_offset) & 0x03;
  return (service EQ ALLOCATED_AND_ACTIVATED);
}


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

    PURPOSE : This function sends a SIM_READ_RECORD_REQ to the SIM card.
              On success this function returns TRUE, otherwise FALSE.

*/
LOCAL BOOL pb_read_sim_record (USHORT data_id, UBYTE rcd_num, UBYTE len)
{
  SHORT table_id;

  TRACE_FUNCTION ("pb_read_sim_record()");

  table_id = psaSIM_atbNewEntry();

  if (table_id NEQ NO_ENTRY)
  {
    /* Allocate room for the SIM data */
    if (pba_data.data EQ NULL)
    {
      ACI_MALLOC (pba_data.data, SIM_MAX_RECORD_SIZE);
    }
    simShrdPrm.atb[table_id].ntryUsdFlg = TRUE;
    simShrdPrm.atb[table_id].accType    = ACT_RD_REC;
    simShrdPrm.atb[table_id].reqDataFld = data_id;
    simShrdPrm.atb[table_id].recNr      = rcd_num;
    if (rcd_num EQ 1)
      simShrdPrm.atb[table_id].dataLen    = NOT_PRESENT_8BIT;
    else
      simShrdPrm.atb[table_id].dataLen    = len;
    simShrdPrm.atb[table_id].exchData   = pba_data.data;
    simShrdPrm.atb[table_id].rplyCB     = pb_read_sim_record_cb;

    simShrdPrm.aId = table_id;

    if (psaSIM_AccessSIMData() < 0)
    {
      TRACE_EVENT("FATAL ERROR");
      return FALSE;
    }
    return TRUE;
  }

  return FALSE;
}


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

    PURPOSE : This function sends a SIM_READ_REQ to the SIM card.
              On success this function returns TRUE, otherwise FALSE.

*/
LOCAL BOOL pb_read_sim (USHORT data_id, UBYTE len, UBYTE max_length)
{
  SHORT table_id;

  TRACE_FUNCTION ("pb_read_sim()");

  table_id = psaSIM_atbNewEntry();

  if (table_id NEQ NO_ENTRY)
  {
    /* Allocate room for the SIM data */
    if (pba_data.data EQ NULL)
    {
      ACI_MALLOC (pba_data.data, SIM_MAX_RECORD_SIZE);
    }

    simShrdPrm.atb[table_id].ntryUsdFlg = TRUE;
    simShrdPrm.atb[table_id].accType    = ACT_RD_DAT;
    simShrdPrm.atb[table_id].reqDataFld = data_id;
    simShrdPrm.atb[table_id].dataOff    = 0;
    simShrdPrm.atb[table_id].dataLen    = len;
    simShrdPrm.atb[table_id].recMax     = max_length;
    simShrdPrm.atb[table_id].exchData   = NULL;
    simShrdPrm.atb[table_id].rplyCB     = pb_read_sim_cb;

    simShrdPrm.aId = table_id;

    if (psaSIM_AccessSIMData() < 0)
    {
      TRACE_EVENT("FATAL ERROR");
      return FALSE;
    }
    return TRUE;
  }
  return FALSE;
}


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

    PURPOSE : This function starts reading the phonebooks based on the 
              information whether the phonebook is activated and allocated
              and still has to be read at all.

*/
LOCAL void pb_read_next_sim_book (void)
{
  TRACE_FUNCTION("pb_read_next_sim_book()");

  /*
   * The next three phonebooks ADN, MSISDN and LND share EXT1 records
   */
  if ((pba_data.fdn_mode NEQ FDN_ENABLE) AND
      pb_sim_service (SRV_ADN) AND !pba_data.book_read[ADN])
  {
    /* Read ADN from SIM card */
    pba_data.current_book = ADN;
    pba_data.book_read[ADN] = TRUE;
    pb_read_sim_record (SIM_ADN, 1, NOT_PRESENT_8BIT);
    return;
  }

  if (pb_sim_service (SRV_MSISDN) AND !pba_data.book_read[UPN])
  {
    /* Read MSISDN from SIM card */
    pba_data.current_book = UPN;
    pba_data.book_read[UPN] = TRUE;
    pb_read_sim_record (SIM_MSISDN, 1, NOT_PRESENT_8BIT);
    return;
  }

#ifdef SIM_LND_SUPPORT
  if (pb_sim_service (SRV_LDN) AND !pba_data.book_read[LDN])
  {
    /* Read LND from SIM card */
    pba_data.current_book = LDN;
    pba_data.book_read[LDN] = TRUE;
    pb_read_sim_record (SIM_LND, 1, NOT_PRESENT_8BIT);
    return;
  }
#endif

#ifdef SIM_LND_SUPPORT
  if (!pb_sim_service (SRV_LDN) AND !pba_data.book_read[LDN])
#else
  if (!pba_data.book_read[LDN])
#endif
  {
    /* 
     * SIM card without LDN service. Create SIM_ICI and SIM_OCI here.
     * Don't create obsolete SIM_LND
     */
    pba_data.book_read[LDN] = TRUE;

    /* Create now also some helper elementary files as of R99+ */
    //(void)pb_sim_create_ef (SIM_ICI, SIZE_EF_ICI, NR_EF_ICI);
    (void)pb_sim_create_ef (FFS_LRN, SIZE_EF_LRN, NR_EF_LRN);
    (void)pb_sim_create_ef (FFS_EXT_LRN, SIZE_EF_EXT_LRN, NR_EF_EXT_LRN);
    (void)pb_sim_create_ef (FFS_LMN, SIZE_EF_LMN, NR_EF_LMN);
    (void)pb_sim_create_ef (FFS_EXT_LMN, SIZE_EF_EXT_LMN, NR_EF_EXT_LMN);

    (void)pb_sim_create_ef (SIM_OCI, SIZE_EF_OCI, NR_EF_OCI);
    (void)pb_sim_create_ef (SIM_EXT5, SIZE_EF_EXT5, NR_EF_EXT5);

    pba_data.phb_record_max[LDN] = NR_EF_OCI;
    pba_data.phb_record_len[LDN] = SIZE_EF_OCI;
    pba_data.book_created[LDN] = TRUE;

    //pba_data.phb_record_max[LRN] = NR_EF_ICI; /* Shared! */
    //pba_data.phb_record_len[LRN] = SIZE_EF_ICI;
    pba_data.phb_record_max[LRN] = NR_EF_LRN;
    pba_data.phb_record_len[LRN] = SIZE_EF_LRN;
    pba_data.book_created[LRN] = TRUE;

    //pba_data.phb_record_max[LMN] = NR_EF_ICI; /* Shared! */
    //pba_data.phb_record_len[LMN] = SIZE_EF_ICI;
    pba_data.phb_record_max[LMN] = NR_EF_LMN;
    pba_data.phb_record_len[LMN] = SIZE_EF_LMN;
    pba_data.book_created[LMN] = TRUE;

    pba_data.ext_record_max[EXT5]    = NR_EF_EXT5;
    pba_data.ext_record_max[EXT_LRN] = NR_EF_EXT_LRN;
    pba_data.ext_record_max[EXT_LMN] = NR_EF_EXT_LMN;

    pba_data.ext_record_max[EXT5]    = SIZE_EF_EXT5;
    pba_data.ext_record_max[EXT_LRN] = SIZE_EF_EXT_LRN;
    pba_data.ext_record_max[EXT_LMN] = SIZE_EF_EXT_LMN;
  }

#ifdef SIM_LND_SUPPORT
  if (pba_data.book_read[ADN] OR
      pba_data.book_read[UPN] OR
      pba_data.book_read[LDN])
#else
  if (pba_data.book_read[ADN] OR
      pba_data.book_read[UPN])
#endif
  {
    /* At least one of the books referencing EXT1 has been read */
    if (!pba_data.ext_created[EXT1] AND
        pb_sim_service (SRV_EXT1))
    {
      /* Reading to get the number of records only */
      pba_data.ext_dummy_read = TRUE;
      pb_read_sim_record (SIM_EXT1, 1, NOT_PRESENT_8BIT);
      return;
    }
  }

  /*
   * FDN phonebook only to be read if ADN is blocked
   */
  if ((pba_data.fdn_mode EQ FDN_ENABLE) AND 
      pb_sim_service (SRV_FDN) AND !pba_data.book_read[FDN])
  {
    /* Read FDN from SIM card */
    pba_data.current_book = FDN;
    pba_data.book_read[FDN] = TRUE;
    pb_read_sim_record (SIM_FDN, 1, NOT_PRESENT_8BIT);
    return;
  }

  if (pba_data.book_read[FDN] AND
      !pba_data.ext_created[EXT2] AND
      pb_sim_service (SRV_EXT2))
  {
    /* Reading to get the number of records only */
    pba_data.ext_dummy_read = TRUE;
    pb_read_sim_record (SIM_EXT2, 1, NOT_PRESENT_8BIT);
    return;
  }

  if (pb_sim_service (SRV_BDN) AND !pba_data.book_read[BDN])
  {
    /* Read BDN from SIM card */
    pba_data.current_book = BDN;
    pba_data.book_read[BDN] = TRUE;
    pb_read_sim_record (SIM_BDN, 1, NOT_PRESENT_8BIT);
    return;
  }

  if (pba_data.book_read[BDN] AND 
      !pba_data.ext_created[EXT4] AND
      pb_sim_service (SRV_EXT4))
  {
    /* Reading to get the number of records only */
    pba_data.ext_dummy_read = TRUE;
    pb_read_sim_record (SIM_EXT4, 1, NOT_PRESENT_8BIT);
    return;
  }

  if (pb_sim_service (SRV_SDN) AND !pba_data.book_read[SDN])
  {
    /* Read SDN from SIM card */
    pba_data.current_book = SDN;
    pba_data.book_read[SDN] = TRUE;
    pb_read_sim_record (SIM_SDN, 1, NOT_PRESENT_8BIT);
    return;
  }

  if (pba_data.book_read[SDN] AND
      !pba_data.ext_created[EXT3] AND
      pb_sim_service (SRV_EXT3))
  {
    /* Reading to get the number of records only */
    pba_data.ext_dummy_read = TRUE;
    pb_read_sim_record (SIM_EXT3, 1, NOT_PRESENT_8BIT);
    return;
  }

  /*
   * For the phonebook entries which reside on SIM but don't have 
   * timestamps some checks could be done here.
   * For example, we could read based on EF_LDN, subsequently we
   * could read the informations solely stored in the flash and
   * then we could decide whether we through away the last dialled 
   * numbers or don't.
   * Probably this is overkill, old Nokia 5110 and more recent Nokia 6330i 
   * simply does not support EF_LND at all but stores the last dialled 
   * numbers in the flash.
   */

#ifdef SIM_TOOLKIT
  if (simShrdPrm.fuRef >= 0)
  {
    psaSAT_FUConfirm (simShrdPrm.fuRef, SIM_FU_SUCC_ADD);
  }
#endif

  /* Free SIM exchange data */
  if (pba_data.data NEQ NULL)
  {
    ACI_MFREE (pba_data.data);
    pba_data.data = NULL;
  }

  SET_PHB_STATE (PHB_STATE_IDLE);

  if (pb_read_queue() EQ PHB_EXCT)
    return; /* Queued LDN entry is written */

  /* Finished reading phonebooks. Flush data. */
  (void)pb_sim_flush_data ();

  /* Inform ACI that we're ready */
  pb_status_ind (PHB_READY, CME_ERR_NotPresent);
}


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

    PURPOSE : This function is the callback when a SIM record has been read
              (SIM_READ_RECORD_CNF).

*/

LOCAL void pb_read_sim_record_cb (SHORT table_id)
{
  T_PHB_RETURN phb_result;  /* Phonebook result code */
  T_PHB_TYPE phb_type;      /* Phonebook type */
  T_EXT_TYPE ext_type;      /* Extension record type */
  T_PHB_TYPE next_phb_type; /* Phonebook type of record to read */
  T_EXT_TYPE next_ext_type; /* Extension record type of record to read */
  USHORT  reqDataFld;       /* requested datafield identifier */
  UBYTE   recNr;            /* record number */
  UBYTE   dataLen;          /* Length of data */
  UBYTE   recMax;           /* Maximum number of records */
  USHORT  errCode;          /* Error code from the SIM */
  USHORT  ext_record_ef;    /* Extension record elementary file id */
  UBYTE   ext_record_no;    /* Extension record number */
  BOOL    changed;          /* Record changed by pb_sim_write_ef() */
  BOOL    write_record;     /* Write the record */

  TRACE_FUNCTION ("pb_read_sim_record_cb()");

  /* 
   * Currently we have a limitation of 254 records in the code.
   * This is because there is a note in 11.11 version 8.3.0 clause 6.4.2
   * that there is a limit of 254 records for linear fixed.
   * Same for USIM looking into 31.102 Annex G.
   * However this constraint is not valid for cyclic, and also this
   * note has not been present in 11.11 version 7.4.0 clause 6.2.4, 
   * there is a limit of 255 records mentioned.
   * Not supporting the 255th record is a minor issue for now.
   * To avoid crashing, we have to limit here for now to 254.
   * This is not a theoretical issue only as there exists SIMs
   * which have 255 ADN entries.
   */
  if (simShrdPrm.atb[table_id].recMax EQ 255)
    simShrdPrm.atb[table_id].recMax = 254;

  /* Get the important information from the read record from T_SIM_ACC_PRM */
  reqDataFld = simShrdPrm.atb[table_id].reqDataFld;
  recNr      = simShrdPrm.atb[table_id].recNr;
  dataLen    = simShrdPrm.atb[table_id].dataLen;
  recMax     = simShrdPrm.atb[table_id].recMax;
  errCode    = simShrdPrm.atb[table_id].errCode;

  phb_type = pb_get_phb_type_from_ef (reqDataFld);
  ext_type = pb_get_ext_type_from_ef (reqDataFld);

  if (phb_type EQ INVALID_PHB AND
      ext_type EQ INVALID_EXT)
  {
    TRACE_ERROR ("Invalid reqDataFld!");
    simShrdPrm.atb[table_id].ntryUsdFlg = FALSE;
    return;
  }

  /* We assume we write the record until we have certain errors */
  write_record = TRUE;

  if (errCode NEQ SIM_NO_ERROR)
  {
    TRACE_EVENT_P1 ("SIM indicated problem code %04x", errCode);
    /* 
     * At least use cleanly deleted data instead of trash.
     */
    memset (pba_data.data, 0xff, SIM_MAX_RECORD_SIZE);
  }

  if (ext_type NEQ INVALID_EXT) 
  {
    /*
     * Got an extension record. 
     */
    if (!pba_data.ext_created[ext_type])
    {
      if (errCode NEQ SIM_NO_ERROR)
      {
        /* We won't create a field on invalid data and won't write */
        write_record = FALSE;
        pba_data.ext_dummy_read = FALSE;
      }
      else
      {
        pba_data.ext_record_max[ext_type] = recMax;
        pba_data.ext_record_len[ext_type] = dataLen;

        /* Shadow field for extension record not yet created */
        (void)pb_sim_create_ef (reqDataFld, dataLen, recMax);
        pba_data.ext_created[ext_type] = TRUE;
      }

      if (pba_data.ext_dummy_read)
      {
        /* 
         * We are reading the first extension record which is assumed 
         * to be not used just to get the number of respective extension 
         * records.
         */
        write_record = FALSE;
        pba_data.ext_dummy_read = FALSE;
      }
    }
  }
  else
  {
    /*
     * Got a normal phonebook record
     */
    if (!pba_data.book_created[phb_type])
    {
      if (errCode NEQ SIM_NO_ERROR)
      {
        /* We won't create a field on invalid data */
        write_record = FALSE;
      }
      else
      {
        pba_data.phb_record_max[phb_type] = recMax;
        pba_data.phb_record_len[phb_type] = dataLen;

        /* Shadow field for normal phonebook record not yet created */
        (void)pb_sim_create_ef (reqDataFld, dataLen, recMax);
        pba_data.book_created[phb_type] = TRUE;

#ifdef SIM_LND_SUPPORT
        if (phb_type EQ LDN)
        {
          /* Create now also some helper elementary files as of R99+ */
          (void)pb_sim_create_ef (SIM_OCI, (USHORT)(dataLen + 14), recMax);
          pba_data.phb_record_max[LDN] = recMax;
          pba_data.phb_record_len[LDN] = dataLen;
          pba_data.book_created[LDN] = TRUE;

          (void)pb_sim_create_ef (SIM_ICI, (USHORT)(dataLen + 14), recMax);
          pba_data.phb_record_max[LRN] = recMax;
          pba_data.phb_record_len[LRN] = dataLen;
          pba_data.book_created[LRN] = TRUE;

          pba_data.phb_record_max[LMN] = recMax;
          pba_data.phb_record_len[LMN] = dataLen;
          pba_data.book_created[LMN] = TRUE;
        }
#endif
      }
    }
  }

  /* 
   * Write raw record. The SIM layer is aware about the data structures
   * and gives us the information whether we have to read an/another
   * extension record now or whether we can continue reading normal
   * phonebook entries.
   */
  if (write_record)
  {
    phb_result = pb_sim_write_ef(reqDataFld,
                                 recNr,
                                 dataLen,
                                 pba_data.data,
                                 &changed,
                                 &ext_record_ef,
                                 &ext_record_no);
  }
  else
  {
    /*
     * New field, record was trash, don't write it, continue reading
     * phonebook entries.
     */
    TRACE_EVENT ("Record not written (trash/dummy ext)");

    phb_result = PHB_OK;
    changed = FALSE;
    ext_record_ef = 0;
    ext_record_no = 0;
  }

  /* Mark SIM table as free now */
  simShrdPrm.atb[table_id].ntryUsdFlg = FALSE;

  if ((GET_PHB_STATE() EQ PHB_STATE_VERIFY) AND
      (phb_result EQ PHB_OK) AND changed)
  {
    /* We have a change detected. Prevent further reading until all read */
    SET_PHB_STATE (PHB_STATE_READ);
  }

  /*
   * Here an optimization is possible. This is, if an extension record
   * and already has been read we could skip reading the extension record
   * here. The optimization is not worth the effort, so we simply don't do
   * it.
   */

  /*
   * Do some protection against garbage
   */
  if (ext_record_ef NEQ 0)
  {
    /* Current record references an extension record */
    next_ext_type = pb_get_ext_type_from_ef (ext_record_ef);

    if ((next_ext_type EQ INVALID_EXT) OR
        (ext_record_no < 1) OR
        (ext_record_no > 245))
    {
      TRACE_ERROR ("pb_sim_write_ef() for ext delivered garbage EXT record");
      ext_record_ef = 0; /* Try to proceed without this garbage */
    }
  }
  
  if (ext_record_ef NEQ 0)
  {
    /* Current record references an extension record */
    next_ext_type = pb_get_ext_type_from_ef (ext_record_ef);

    /* 
     * We have to read an extension record.
     */
    if (ext_type NEQ INVALID_EXT)
    {
      /*
       * It's the first extension record. Remember the paused phonebook.
       */
      TRACE_EVENT_P4 ("PHB EF %04x record %d references EXT EF %04x record %d",
                      reqDataFld,
                      recNr,
                      ext_record_ef,
                      ext_record_ef);
      pba_data.paused_ef = reqDataFld;
      pba_data.paused_no = recNr;
    }

    /* Read the extension record. */
    if (pba_data.ext_created[next_ext_type])
    {
      (void)pb_read_sim_record (ext_record_ef,
                                ext_record_no, 
                                pba_data.ext_record_len[next_ext_type]);
    }
    else
    {
      (void)pb_read_sim_record (ext_record_ef,
                                ext_record_no, 
                                NOT_PRESENT_8BIT);
    }
  }
  else if (ext_type NEQ INVALID_EXT)
  {
    /* 
     * The record read was the last extension record in the chain.
     * Come back reading normal phonebook entries.
     */

    next_phb_type = pb_get_phb_type_from_ef (pba_data.paused_ef);
    //////(void)pb_sim_build_index (next_phb_type);
    if (pba_data.paused_no < pba_data.phb_record_max[next_phb_type])
    {
      /* Continue reading the current phonebook */
      (void)pb_read_sim_record (pba_data.paused_ef,
                                (UBYTE)(pba_data.paused_no + 1),
                                pba_data.phb_record_len[next_phb_type]);
    }
    else
    {
      ////// Update index - for now here
      (void)pb_sim_build_index (phb_type);

      /* Next book */
      pb_read_next_sim_book ();
    }
    pba_data.paused_ef  = 0;
    pba_data.paused_no  = 0;

  }
  else if (simShrdPrm.atb[table_id].recNr < simShrdPrm.atb[table_id].recMax)
  {
    /*
     * Normal phonebook record read, no extension referenced and not
     * finished. Continue reading the current phonebook 
     */
    //////(void)pb_sim_build_index (phb_type);

    (void)pb_read_sim_record (simShrdPrm.atb[table_id].reqDataFld,
                              (UBYTE)(simShrdPrm.atb[table_id].recNr + 1),
                              simShrdPrm.atb[table_id].dataLen);
  }
  else
  {
    /*
     * Normal phonebook record read, no extension referenced and finished
     * reading this book. Continue reading the next book.
     */
    (void)pb_sim_build_index (phb_type);

    /* Next book */
    pb_read_next_sim_book ();
  }
}


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

    PURPOSE : This function marks the given elementary file as unread.
    
*/
LOCAL void pb_sat_update_reset (USHORT ef)
{
  T_PHB_TYPE phb_type;

  TRACE_FUNCTION ("pb_sat_update_reset()");

  phb_type = pb_get_phb_type_from_ef (ef);

  if (phb_type NEQ INVALID_PHB)
  {
    /* Mark book as unread */
    pba_data.book_read[phb_type] = FALSE;
  }
}


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

    PURPOSE : This function is the callback when a SIM elementary file has 
              been read (SIM_READ_CNF).
              There are two fields which can have been read here, those are
              the emergency call numbers and the SIM service table.

*/


LOCAL void pb_read_sim_cb (SHORT table_id)
{
  TRACE_FUNCTION ("pb_read_sim_cb()");

  switch (simShrdPrm.atb[table_id].reqDataFld)
  {
    case SIM_ECC:
      if (simShrdPrm.atb[table_id].errCode EQ SIM_NO_ERROR)
      {
        (void)pb_sim_set_ecc (simShrdPrm.atb[table_id].dataLen,
                              simShrdPrm.atb[table_id].exchData);

#ifdef SIM_TOOLKIT
        if (simShrdPrm.fuRef >= 0)
        {
          psaSAT_FUConfirm (simShrdPrm.fuRef, SIM_FU_SUCCESS);
        }
#endif /* #ifdef SIM_TOOLKIT */

      }        
      else
      {
        /* 
         * Some error from the SIM. Indicate a fallback to the PCM/FFS.
         */
        (void)pb_sim_set_ecc (0, NULL);

#ifdef SIM_TOOLKIT
        if (simShrdPrm.fuRef >= 0)
        {
          psaSAT_FUConfirm (simShrdPrm.fuRef, SIM_FU_ERROR);
        }
#endif

      }

      if (pba_data.data NEQ NULL)
      {
        ACI_MFREE (pba_data.data);
        pba_data.data = NULL;
      }
      break;

    case SIM_SST:
      if (simShrdPrm.atb[table_id].errCode EQ SIM_NO_ERROR)
      {
        /* copy SIM service table */
        memset(pba_data.sim_service_table, 0, MAX_SRV_TBL);
        memcpy(pba_data.sim_service_table, 
               simShrdPrm.atb[table_id].exchData, 
               MINIMUM(MAX_SRV_TBL, simShrdPrm.atb[table_id].dataLen));

        /* start reading SIM phonebook */
        simShrdPrm.atb[table_id].ntryUsdFlg = FALSE;
        pb_start_build (FALSE);
      }
      else
      {
        TRACE_ERROR ("Cannot read SIM service table, using old table");

#ifdef SIM_TOOLKIT
        if (simShrdPrm.fuRef >= 0)
        {
          psaSAT_FUConfirm (simShrdPrm.fuRef, SIM_FU_ERROR);
        }
#endif

        /* start reading SIM phonebook (fallback action, best effort) */
        simShrdPrm.atb[table_id].ntryUsdFlg = FALSE;
        pb_start_build (FALSE);
      }
      break;

    default:
      TRACE_ERROR ("Unexpected SIM_READ_CNF");
      break;
  }
  simShrdPrm.atb[table_id].ntryUsdFlg = FALSE;
}


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

    PURPOSE : This function determines wheter an elementary file has to 
              be synchronized to the SIM or only exists locally in the FFS.

*/
LOCAL BOOL pb_is_sim_field (USHORT ef)
{
  TRACE_FUNCTION ("pb_is_sim_field()");

  switch (ef)
  {
    //case SIM_ICI:
    case FFS_LRN:
    case FFS_LMN:
    case FFS_EXT_LRN:
    case FFS_EXT_LMN:
    case SIM_OCI:
    case SIM_EXT5:
      return FALSE; /* Only in FFS */

    default:
      return TRUE;  /* Counterpart in SIM exists */
  }
}


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

    PURPOSE : This function synchronizes a changed record to the SIM.
              
              The first parameter is given to determine whether it's the first
              record to be synchronized, in this case ACI has to be informed.
  
              If there was no record to synchronize, PHB_OK is returned.
              If there is a record to synchronize, PHB_EXCT is returned.
              If there was an internal failure we get PHB_FAIL 
              (which is unexpected).

*/

LOCAL T_PHB_RETURN pb_sync_next_sim_record (BOOL first)
{
  T_PHB_RETURN phb_result;
  USHORT field_id;
  USHORT record;
  USHORT entry_size;

  TRACE_FUNCTION ("pb_sync_next_sim_record()");

  do
  {
    if (pba_data.records_changed.entries EQ 0)
    {
      SET_PHB_STATE (PHB_STATE_IDLE);

      if (pb_read_queue() EQ PHB_EXCT)
        return PHB_EXCT; /* Queued LDN entry is written */

      /* Inform SIM layer we're done synchronizing */
      (void)pb_sim_flush_data ();

      /* Inform ACI that we're ready */
      pb_status_ind (PHB_READY, CME_ERR_NotPresent);

      return PHB_OK; /* No record to synchronize to the SIM */
    }

    /* Get the next record from the list */
    pba_data.records_changed.entries--;
    field_id = pba_data.records_changed.field_id[0];
    record   = pba_data.records_changed.record[0];

    /* Remove the record from the list */
    memmove (&pba_data.records_changed.field_id[0],
             &pba_data.records_changed.field_id[1],
             sizeof (USHORT) * (DB_MAX_AFFECTED - 1));
    memmove (&pba_data.records_changed.record[0],
             &pba_data.records_changed.record[1],
             sizeof (USHORT) * (DB_MAX_AFFECTED - 1));
  } 
  while (!pb_is_sim_field(field_id));

  TRACE_EVENT_P3 ("Synch ef %04X record %d, still %d entries to do",
                  field_id, record, pba_data.records_changed.entries);

  /* Allocate room for the SIM data */ // #HM# Check for free
  if (pba_data.data EQ NULL)
  {
    ACI_MALLOC (pba_data.data, SIM_MAX_RECORD_SIZE);
  }

  /* Fetch record in raw form from the database */
  phb_result = pb_sim_read_ef (field_id,
                               record,
                               &entry_size,
                               pba_data.data);

  if (phb_result EQ PHB_OK)
  {
    /* And write it to the SIM */
    phb_result = pb_write_sim_record (field_id,
                                      (UBYTE)record, 
                                      (UBYTE)entry_size,
                                      pba_data.data);

    if (phb_result NEQ PHB_EXCT)
    {
      /* Inform ACI that we've had a problem */
      pb_status_ind ( PHB_WRITE_FAIL, CME_ERR_NotPresent );
      return phb_result; /* Some unexpected internal failure */
    }

    if (first)
    {
      /* Inform ACI that we're busy synchronizing SIM entries */
      pb_status_ind ( PHB_BUSY, CME_ERR_NotPresent );
    }

    return PHB_EXCT;
  }
  return phb_result;
}


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

    PURPOSE : This function writes a SIM record to the SIM.

*/

LOCAL T_PHB_RETURN pb_write_sim_record (USHORT ef,
                                        UBYTE phy_recno,
                                        UBYTE entry_size,
                                  const UBYTE *buffer)
{
  SHORT           table_id;


  TRACE_FUNCTION("pb_write_sim_record()");

  table_id = psaSIM_atbNewEntry();
  if (table_id EQ NO_ENTRY)
  {
    TRACE_ERROR ("pb_write_sim_record(): no more table entries");
    return (PHB_FAIL);
  }

  simShrdPrm.atb[table_id].ntryUsdFlg = TRUE;
  simShrdPrm.atb[table_id].accType    = ACT_WR_REC;
  simShrdPrm.atb[table_id].reqDataFld = ef;
  simShrdPrm.atb[table_id].recNr      = phy_recno;
  simShrdPrm.atb[table_id].dataLen    = entry_size;
  simShrdPrm.atb[table_id].exchData   = (UBYTE *)buffer;
  simShrdPrm.atb[table_id].rplyCB     = pb_write_sim_record_cb;

  simShrdPrm.aId = table_id;

  if (psaSIM_AccessSIMData() < 0)
  {
    TRACE_EVENT("pb_write_sim_record(): psaSIM_AccessSIMData() failed");
    return (PHB_FAIL);
  }

  return (PHB_EXCT);
}


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

    PURPOSE : This function is the callback when a SIM record has been
              written to the SIM.

*/

LOCAL void pb_write_sim_record_cb (SHORT table_id)
{
  UBYTE max_record;

  TRACE_FUNCTION("pb_write_sim_record_cb()");

  simShrdPrm.atb[table_id].ntryUsdFlg = FALSE;

  if (simShrdPrm.atb[table_id].errCode NEQ SIM_NO_ERROR)
  {
    /* 
     * Data in the database and on the SIM probably now differ,
     * we can do not much about this.
     */
    TRACE_ERROR("pb_write_cb(): error for writing");
    pb_status_ind (PHB_WRITE_FAIL,
                   (SHORT)cmhSIM_GetCmeFromSim (simShrdPrm.atb[table_id].errCode));
    return;
  }

  switch (GET_PHB_STATE())
  {
    case PHB_STATE_WRITE:
      /* Synchronize next SIM record */
      switch (pb_sync_next_sim_record (FALSE))
      {
        case PHB_OK: /* All done */
          /* Inform the ACI that we're done */
          pb_status_ind (PHB_READY, CME_ERR_NotPresent);
          break;

        case PHB_EXCT:
          break; /* Next record => SIM */

        case PHB_FAIL:
        default:
          TRACE_ERROR ("Unexpected phb_result");      
          break;
      }
      break;

    case PHB_STATE_DELETE_BOOK:
      /* Synchronize next SIM record */
      switch (pb_sync_next_sim_record (FALSE))
      {
        case PHB_OK:
          /* 
           * Record synched. Delete next record of phonebook, if available
           */
          max_record = pba_data.phb_record_max[pba_data.current_book];
          while ((pba_data.del_record <= max_record) AND
                 (pba_data.records_changed.entries EQ 0))
          {
            /* Delete the record in the database */
            if (pb_sim_del_record (pba_data.current_book, 
                                   pba_data.del_record,
                                   &pba_data.records_changed) NEQ PHB_OK)
            {
              TRACE_ERROR ("Unexpected problem from pb_sim_del_record()");

              /* Inform SIM layer we're done synchronizing.
               * Should we indicate also there was a problem? */
              (void)pb_sim_flush_data ();

              pb_status_ind (PHB_WRITE_FAIL, CME_ERR_NotPresent);

              SET_PHB_STATE (PHB_STATE_IDLE);
              return;
            }
            pba_data.del_record++;
          } 

          if (pba_data.records_changed.entries NEQ 0)
          {
            /* Synchronizing record(s) to the SIM */
            if (pb_sync_next_sim_record (FALSE) NEQ PHB_EXCT)
            {
              TRACE_ERROR ("Unexpected problem from pb_sync_next_sim_record()");

              /* Inform SIM layer we're done synchronizing.
               * Should we indicate also there was a problem? */
              (void)pb_sim_flush_data ();

              pb_status_ind (PHB_WRITE_FAIL, CME_ERR_NotPresent);

              SET_PHB_STATE (PHB_STATE_IDLE);
              return;
            }
          }

          /* 
           * We're through
           */

          (void)pb_sim_flush_data ();

          /* Inform the ACI that we're done */
          pb_status_ind (PHB_READY, CME_ERR_NotPresent);

          SET_PHB_STATE (PHB_STATE_IDLE);
          break;

        case PHB_EXCT:
          break; /* Next record => SIM */

        case PHB_FAIL:
        default:
          TRACE_ERROR ("Unexpected problem synching SIM");      

          (void)pb_sim_flush_data ();

          pb_status_ind (PHB_WRITE_FAIL, CME_ERR_NotPresent);
          /* Inform SIM layer we're done synchronizing.
           * Should we indicate also there was a problem? */

          SET_PHB_STATE (PHB_STATE_IDLE);
          break;
      }
      break;

    default:
      TRACE_ERROR ("Unexpected default");
      break;
  }
}


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

  PURPOSE : This function converts a SIM elementary file number to the 
            appropriate constant of type T_PHB_TYPE, if applicable.
            Non applicable => INVALID_PHB

*/
T_PHB_TYPE pb_get_phb_type_from_ef (USHORT ef)
{
  TRACE_FUNCTION ("pb_get_phb_type_from_ef()");

  switch (ef)
  {
    case SIM_ECC:
      return ECC;

    case SIM_ADN:
      return ADN;

    case SIM_FDN:
      return FDN;

    case SIM_BDN:
      return BDN;

    case SIM_LND:
      return LDN;

    case SIM_MSISDN:
      return UPN;

    case SIM_SDN:
      return SDN;

    default:
      return INVALID_PHB;
  }
}

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

  PURPOSE : This function converts a SIM elementary file number to the
            appropriate constant of T_PHB_EXT_TYPE, if applicable.
            Non applicable => INVALID_EXT

*/
T_EXT_TYPE pb_get_ext_type_from_ef (USHORT ef)
{
  TRACE_FUNCTION ("pb_get_ext_type_from_ef()");

  switch (ef)
  {
    case SIM_EXT1:
      return EXT1;

    case SIM_EXT2:
      return EXT2;

    case SIM_EXT3:
      return EXT3;

    case SIM_EXT4:
      return EXT4;

    case SIM_EXT5:
      return EXT5;

    default:
      return INVALID_EXT;
  }
}


/* ------------------------------------------------------------------------ */

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

  PURPOSE : At some places a more sohisticated number searching function 
            is desirable which can be based on existing phonebook 
            functionality.

*/
GLOBAL T_PHB_RETURN pb_search_number_ex (T_PHB_TYPE type,
                                         const UBYTE *number,
                                         SHORT *order_num,
                                         SHORT *found,
                                         T_PHB_RECORD *entry)
{
  T_PHB_RETURN phb_result;
  SHORT index;

  TRACE_FUNCTION ("pb_search_number_ex()");

  /* Search for first entry */
  phb_result = pb_search_number (type,
                                 number,
                                 order_num);
  if (phb_result NEQ PHB_OK)
    return phb_result;

  /* 
   * Determine number of found entries. We are doing a linear algorithm, 
   * a binary algorithm is possible here to speed up things.
   */
  if (found NEQ NULL)
  {
    *found = 1;
    index = *order_num;

    while (pb_search_number (type, number, &index) EQ PHB_OK)
    { 
      ++(*found);
      /* index should not be incremented as lower layer (DB) will take
         care of it */
    }
  }

  /* Read the first found entry, if desired */
  if (entry NEQ NULL)
    return (pb_read_number_record (type, *order_num, entry));
  return PHB_OK;
}


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

  PURPOSE : At some places a more sohisticated name searching function 
            is desirable which can be based on existing phonebook 
            functionality.

*/
GLOBAL T_PHB_RETURN pb_search_name_ex (T_PHB_TYPE type,
                                       T_PHB_MATCH match,
                                       const T_ACI_PB_TEXT *search_tag,
                                       SHORT *order_num,
                                       SHORT *found,
                                       T_PHB_RECORD *entry)
{
  T_PHB_RETURN phb_result;
  SHORT index;

  TRACE_FUNCTION ("pb_search_name_ex()");

  /* Search for first entry */
  phb_result = pb_search_name (type,
                               match,
                               search_tag,
                               order_num);
  if (phb_result NEQ PHB_OK)
    return phb_result;

  /* 
   * Determine number of found entries. We are doing a linear algorithm, 
   * a binary algorithm is possible here to speed up things.
   */
  if (found NEQ NULL)
  {
    *found = 1;
    index = *order_num;

    while (pb_search_name (type, match, search_tag, &index) EQ PHB_OK)
    { 
      ++(*found);
      /* index should not be incremented as lower layer (DB) will take
         care of it */
    }
      
  }

  /* Read the first found entry, if desired */
  if (entry NEQ NULL)
    return (pb_read_alpha_record (type, *order_num, entry));
  return PHB_OK;
}


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

  PURPOSE : This function indicates the new phonebook status to the ACI 
            preventing we're indication twice PHB_READY or PHB_BUSY.

*/
LOCAL void pb_status_ind (T_PHB_STAT phb_stat, SHORT cmeError)
{
  TRACE_FUNCTION ("pb_status_ind()");

  /* Avoid to indicate twice busy or ready */
  if ((pba_data.phb_stat NEQ phb_stat) OR
      ((phb_stat NEQ PHB_READY) AND
       (phb_stat NEQ PHB_BUSY)))
  {
    cmhPHB_StatIndication ((UBYTE)phb_stat, cmeError,TRUE);
    pba_data.phb_stat = phb_stat;
  }
}


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

  PURPOSE : This function writes a LDN entry to the queue if the SIM is busy.

*/
LOCAL T_PHB_RETURN pb_write_queue (const T_PHB_RECORD *entry)
{
  TRACE_FUNCTION ("pb_write_queue()");

  if (pba_data.c_queued < PHB_MAX_QUEUE)
  {
    memmove (&pba_data.queued[1],
             &pba_data.queued[0],
             pba_data.c_queued * sizeof (T_PHB_QUEUE *));
    ACI_MALLOC (pba_data.queued[pba_data.c_queued], 
                sizeof (T_PHB_QUEUE));
    pba_data.queued[0]->entry = *entry;
    pba_data.c_queued++;
    return PHB_EXCT;
  }
  
  return PHB_FAIL; /* Queue full */
}


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

  PURPOSE : This function clears the LDN queue.

*/
LOCAL void pb_clear_queue (void)
{
  TRACE_FUNCTION ("pb_clear_queue()");

  while (pba_data.c_queued NEQ 0)
  {
    pba_data.c_queued--;
    ACI_MFREE (pba_data.queued[pba_data.c_queued]);
    pba_data.queued[pba_data.c_queued] = NULL;
  }
}


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

  PURPOSE : This function reads the queue and adds the queued phonebook 
            entries.

*/
LOCAL T_PHB_RETURN pb_read_queue (void)
{
  T_PHB_RETURN phb_result;

  TRACE_FUNCTION ("pb_write_queue()");

  while (pba_data.c_queued NEQ 0)
  {
    /*
     * LDN entries have been queued. Write them now.
     */
    pba_data.c_queued--;
    phb_result = pb_add_record (LDN,
                                pba_data.queued[pba_data.c_queued]->index,
                                &pba_data.queued[pba_data.c_queued]->entry);
    ACI_MFREE (pba_data.queued[pba_data.c_queued]);
    pba_data.queued[pba_data.c_queued] = NULL;
    switch (phb_result)
    {
      case PHB_EXCT:
        return phb_result;
      case PHB_OK:
        break;
      default:
        pb_clear_queue();
        return phb_result;
    }
  }
  return PHB_OK;
}

#endif /* #ifdef TI_PS_FFS_PHB */
