view src/g23m-gprs/gmm/gmm_pei.c @ 662:8cd8fd15a095

SIM speed enhancement re-enabled and made configurable TI's original code supported SIM speed enhancement, but Openmoko had it disabled, and OM's disabling of speed enhancement somehow caused certain SIM cards to start working which didn't work before (OM's bug #666). Because our FC community is much smaller in year 2020 than OM's community was in their day, we are not able to find one of those #666-affected SIMs, thus the real issue they had encountered remains elusive. Thus our solution is to re-enable SIM speed enhancement and simply wait for if and when someone runs into a #666-affected SIM once again. We provide a SIM_allow_speed_enhancement global variable that allows SIM speed enhancement to be enabled or disabled per session, and an /etc/SIM_spenh file in FFS that allows it to enabled or disabled on a non-volatile basis. SIM speed enhancement is now enabled by default.
author Mychaela Falconia <falcon@freecalypso.org>
date Sun, 24 May 2020 05:02:28 +0000
parents 219afcfc6250
children
line wrap: on
line source

/* 
+----------------------------------------------------------------------------- 
|  Project :  GPRS (8441)
|  Modul   :  gmm_pei.c
+----------------------------------------------------------------------------- 
|  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 implements the process body interface
|             for the entity GPRS Mobility Management (GMM)
|             
|             Exported functions:
|             
|             pei_create    - Create the Protocol Stack Entity
|             pei_init      - Initialize Protocol Stack Entity
|             pei_primitive - Process Primitive
|             pei_timeout   - Process Timeout
|             pei_exit      - Close resources and terminate
|             pei_run       - Process Primitive
|             pei_config    - Dynamic Configuration
|             pei_monitor   - Monitoring of physical Parameters
+----------------------------------------------------------------------------- 
*/ 


#define GMM_PEI_C

#define ENTITY_GMM

/*==== INCLUDES =============================================================*/

#include <stddef.h>     /* to get definition of offsetof(), for MAK_FUNC_S */
#include <stdlib.h>     /* to get atoi for tokenizer in pei_init */
#include "typedefs.h"   /* to get Condat data types */
#include "vsi.h"        /* to get a lot of macros */
#include "macdef.h"
#include "gprs.h"
#include "gsm.h"        /* to get a lot of macros */
#include "cnf_gmm.h"    /* to get cnf-definitions */
#include "mon_gmm.h"    /* to get mon-definitions */
#include "prim.h"       /* to get the definitions of used SAP and directions */
#include "pei.h"        /* to get PEI interface */
#include "gmm.h"        /* to get the global entity definitions */
#include "gmm_f.h"      /* to get the debug print function */

#include "gmm_kernp.h"  /* to get primitive interface to KERN */
#include "gmm_txp.h"    /* to get primitive interface to TX */
#include "gmm_rxp.h"    /* to get primitive interface to RX */
#include "gmm_rdyp.h"   /* to get primitive interface to RDY */
#include "gmm_syncp.h"  /* to get primitive interface to SYNC */

#include "gmm_kernf.h"  /* to get functions from KERN */
#include "gmm_txf.h"    /* to get functions from TX */
#include "gmm_rxf.h"    /* to get functions from RX */
#include "gmm_rdyf.h"   /* to get functions from RDY */
#include "gmm_syncf.h"  /* to get functions from SYNC */


#include "ccdapi.h"     /* to get ccd stuff */
#include "tok.h"        /* to get tokenizer */
#include  <string.h>    /* to get memcpy */

#include "gmm_em.h"     /*to get definitions of the Engineering Mode*/
/*==== DEFINITIONS ==========================================================*/

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

/*==== GLOBAL VARS ==========================================================*/

/*==== LOCAL VARS ===========================================================*/

static  BOOL          first_access  = TRUE;
static  T_MONITOR     gmm_mon;

/*
 * Jumptables to primitive handler functions. One table per SAP.
 *
 * Use MAK_FUNC_0 for primitives which contains no SDU.
 * Use MAK_FUNC_S for primitives which contains a SDU.
 */

/*******************************************************
 * SAVE prim
 *******************************************************/

#define PEI_SIGNAL_NOT_SAVED      FALSE
#define PEI_SIGNAL_SAVED          TRUE
#define PEI_END_SAVETAB           0xFF

#define GMM_MAX_SIGNALS_SAVED      5     /* value adjusted to GMM requirements */

typedef struct SAVE_QUEUE
{
  void*              prim;
  T_VOID_FUNC        func;
  struct SAVE_QUEUE* next;
} T_SAVE_QUEUE;

void gmm_pei_delete_queue( T_SAVE_QUEUE** queue );

typedef struct
{
  USHORT  state;
  USHORT  signal[GMM_MAX_SIGNALS_SAVED];
} T_SAVE_TAB;

/*==== LOCAL PROTOTYPES =====================================================*/
LOCAL void gmm_pei_handle_prim( T_VOID_FUNC func, void* prim );
LOCAL BOOL gmm_pei_handle_save( void* prim, T_SAVE_QUEUE** queue,
                                     const T_SAVE_TAB* table, T_VOID_FUNC func);
LOCAL void gmm_pei_handle_queue( T_SAVE_QUEUE** queue,
                                      const T_SAVE_TAB* table);

/*==== VAR LOCAL ============================================================*/

LOCAL T_SAVE_QUEUE*     save_queue;     /* SAVE queue hangers */

LOCAL const T_SAVE_TAB save_tab[] = {

  { KERN_GMM_REG_INITIATED,      
                            {MMGMM_LUP_NEEDED_IND,
                             GMMREG_NET_REQ, 
                             GMMREG_PLMN_RES,
                             0, 0 }
  },

  { KERN_GMM_RAU_INITIATED,      
                            {MMGMM_LUP_NEEDED_IND,
                             GMMREG_DETACH_REQ,
                             GMMREG_NET_REQ, 
                             GMMREG_PLMN_RES,
                             0 }
  },

  {KERN_GMM_RAU_WAIT_FOR_NPDU_LIST ,
                           {GMMRR_CELL_IND, 
                            0, 0, 0, 0 }
  },
  { KERN_GMM_DEREG_SUSPENDING,
                            {GMMREG_NET_REQ, 
                             GMMREG_PLMN_RES,
                             GMMRR_CS_PAGE_IND,
                             MMGMM_CM_ESTABLISH_IND,
                             MMGMM_CM_EMERGENCY_IND     }
  },
  { KERN_GMM_REG_SUSPENDING,
                            {GMMREG_NET_REQ, 
                             GMMREG_PLMN_RES,
                             GMMRR_CS_PAGE_IND,
                             MMGMM_CM_ESTABLISH_IND,
                             MMGMM_CM_EMERGENCY_IND     }
  },
  { KERN_GMM_REG_RESUMING,
                            {GMMREG_DETACH_REQ, 
                             GMMRR_CS_PAGE_IND,
                             MMGMM_CM_ESTABLISH_IND,
                             MMGMM_CM_EMERGENCY_IND,
                             GMMREG_ATTACH_REQ }
  },
  { KERN_GMM_DEREG_RESUMING,
                            {GMMREG_DETACH_REQ, 
                             GMMRR_CS_PAGE_IND,
                             MMGMM_CM_ESTABLISH_IND,
                             MMGMM_CM_EMERGENCY_IND,
                             GMMREG_ATTACH_REQ}
  },
  { PEI_END_SAVETAB,
                           { 0, 0, 0, 0, 0 }
  }
};
/*******************************************************
 * End SAVE prim
 *******************************************************/


/*
 * Function is needed for grr_table[]. This declaration can be removed
 * as soon as this function is no more called (i.e. all primitives are
 * handled).
 */
LOCAL void primitive_not_supported (void *data);

static const T_FUNC gmmreg_table[] =
{
  MAK_FUNC_0(kern_gmmreg_attach_req,        GMMREG_ATTACH_REQ),
  MAK_FUNC_0(kern_gmmreg_detach_req,        GMMREG_DETACH_REQ),
  MAK_FUNC_0(kern_gmmreg_net_req,           GMMREG_NET_REQ),
  MAK_FUNC_0(kern_gmmreg_plmn_res,          GMMREG_PLMN_RES),
  MAK_FUNC_0(kern_gmmreg_plmn_mode_req,     GMMREG_PLMN_MODE_REQ),
  MAK_FUNC_0(kern_gmmreg_config_req,        GMMREG_CONFIG_REQ),
};

#ifdef FF_EM_MODE
static const T_FUNC em_ul_table[] =
{
  MAK_FUNC_N(primitive_not_supported,       0                  ), /* 0x00 */
  /*EM_SC_GPRS_INFO_REQ cannot be sent directly from ACI to GRR, because no SAP is defined.
  So it have to be passed via GMM.*/
  MAK_FUNC_0(em_gmm_sc_gprs_info_req,       EM_SC_GPRS_INFO_REQ     ), /* 0x01 */
  MAK_FUNC_N(primitive_not_supported,       0                  ), /* 0x02 */
  MAK_FUNC_N(primitive_not_supported,       0                  ), /* 0x03 */
  MAK_FUNC_N(primitive_not_supported,       0                  ), /* 0x04 */
  MAK_FUNC_N(primitive_not_supported,       0                  ), /* 0x05 */
  MAK_FUNC_N(primitive_not_supported,       0                  ), /* 0x06 */
  MAK_FUNC_N(primitive_not_supported,       0                  ), /* 0x07 */
  MAK_FUNC_N(primitive_not_supported,       0                  ), /* 0x08 */
  MAK_FUNC_N(primitive_not_supported,       0                  ), /* 0x09 */
  MAK_FUNC_N(primitive_not_supported,       0                  ), /* 0x0A */
  MAK_FUNC_N(primitive_not_supported,       0                  ), /* 0x0B */
  MAK_FUNC_N(primitive_not_supported,       0                  ), /* 0x0C */
  MAK_FUNC_N(primitive_not_supported,       0                  ), /* 0x0D */
  MAK_FUNC_N(primitive_not_supported,       0                  ), /* 0x0E */
  MAK_FUNC_N(primitive_not_supported,       0                  ), /* 0x0F */
  MAK_FUNC_N(primitive_not_supported,       0                  ), /* 0x10 */
  MAK_FUNC_0(em_gmm_pco_trace_req   ,       EM_PCO_TRACE_REQ   ), /* 0x11*/ /*PCO output*/
  MAK_FUNC_N(primitive_not_supported,       0                  ), /* 0x12 */
  MAK_FUNC_N(primitive_not_supported,       0                  ), /* 0x13 */
  MAK_FUNC_N(primitive_not_supported,       0                  ), /* 0x14 */
  MAK_FUNC_N(primitive_not_supported,       0                  ), /* 0x15 */
  MAK_FUNC_N(primitive_not_supported,       0                  ), /* 0x16 */
  MAK_FUNC_0(em_gmm_info_req,                EM_GMM_INFO_REQ   ), /* 0x17 */
  /*EM_GRLC_INFO_REQ cannot be sent directly from ACI to GRLC, so it is passed via GMM*/
  MAK_FUNC_0(em_gmm_grlc_info_req,          EM_GRLC_INFO_REQ        ),  /* 0x18 */
  MAK_FUNC_N(primitive_not_supported,       0                       ), /* 0x19 */
  MAK_FUNC_0(em_gmm_grr_event_req,          EM_GRR_EVENT_REQ        ), /* 0x1A */
  MAK_FUNC_0(em_gmm_event_req,              EM_GMM_EVENT_REQ        ), /* 0x1B */
  MAK_FUNC_0(em_gmm_grlc_event_req,         EM_GRLC_EVENT_REQ       ), /* 0x1C */
  MAK_FUNC_0(em_gmm_throughput_info_req,    EM_THROUGHPUT_INFO_REQ  ) /* 0x1D */
};

static const T_FUNC em_dl_table[] =
{
  MAK_FUNC_N(primitive_not_supported,       0                  ), /* 0x00 */
/*EM_SC_GPRS_INFO_CNF cannot be sent directly from GRR to ACI, so it is passed via GMM*/
  MAK_FUNC_0(em_gmm_sc_gprs_info_cnf,       EM_SC_GPRS_INFO_CNF      ), /* 0x01 */
  MAK_FUNC_N(primitive_not_supported,       0                  ), /* 0x02 */
  MAK_FUNC_N(primitive_not_supported,       0                  ), /* 0x03 */
  MAK_FUNC_N(primitive_not_supported,       0                  ), /* 0x04 */
  MAK_FUNC_N(primitive_not_supported,       0                  ), /* 0x05 */
  MAK_FUNC_N(primitive_not_supported,       0                  ), /* 0x06 */
  MAK_FUNC_N(primitive_not_supported,       0                  ), /* 0x07 */
  MAK_FUNC_N(primitive_not_supported,       0                  ), /* 0x08 */
  MAK_FUNC_N(primitive_not_supported,       0                  ), /* 0x09 */
  MAK_FUNC_N(primitive_not_supported,       0                  ), /* 0x0A */
  MAK_FUNC_N(primitive_not_supported,       0                  ), /* 0x0B */
  MAK_FUNC_N(primitive_not_supported,       0                  ), /* 0x0C */
  MAK_FUNC_N(primitive_not_supported,       0                  ), /* 0x0D */
  MAK_FUNC_N(primitive_not_supported,       0                  ), /* 0x0E */
  MAK_FUNC_N(primitive_not_supported,       0                  ), /* 0x0F */
  MAK_FUNC_N(primitive_not_supported,       0                  ), /* 0x10 */
  MAK_FUNC_N(primitive_not_supported,       0                  ), /* 0x11 */
  MAK_FUNC_N(primitive_not_supported,       0                  ), /* 0x12 */
  MAK_FUNC_N(primitive_not_supported,       0                  ), /* 0x13 */
  MAK_FUNC_N(primitive_not_supported,       0                  ), /* 0x14 */
  MAK_FUNC_N(primitive_not_supported,       0                  ), /* 0x15 */
  MAK_FUNC_N(primitive_not_supported,       0                  ), /* 0x16 */
  MAK_FUNC_N(primitive_not_supported,       0                  ), /* 0x17 */
/*EM_GRLC_INFO_CNF cannot be sent directly from ACI to GRLC, so it is passed via GMM*/
  MAK_FUNC_0(em_gmm_grlc_info_cnf,          EM_GRLC_INFO_CNF         ), /* 0x18 */
  MAK_FUNC_N(primitive_not_supported,       0                        ), /* 0x19 */
  MAK_FUNC_N(primitive_not_supported,       0                        ), /* 0x1A */
  MAK_FUNC_N(primitive_not_supported,       0                        ), /* 0x1B */
  MAK_FUNC_N(primitive_not_supported,       0                        ), /* 0x1C */
  MAK_FUNC_0(em_gmm_throughput_info_cnf,    EM_THROUGHPUT_INFO_CNF   ) /* 0x1D */
};
#endif /* FF_EM_MODE */  


static const T_FUNC gmmrr_table[] =
{
  MAK_FUNC_0(sync_gmmrr_cell_ind,           GMMRR_CELL_IND),
  MAK_FUNC_N(primitive_not_supported,       0                  ),
  MAK_FUNC_0(kern_gmmrr_page_ind,           GMMRR_PAGE_IND),
  MAK_FUNC_0(kern_gmmrr_cs_page_ind,        GMMRR_CS_PAGE_IND),
  MAK_FUNC_0(kern_gmmrr_suspend_cnf,        GMMRR_SUSPEND_CNF),
  MAK_FUNC_N(primitive_not_supported,       0                  ), /* TCS 2.1 */
  MAK_FUNC_N(primitive_not_supported,       0                  ),/* TCS 2.1 */
  MAK_FUNC_N(primitive_not_supported,       0                  ), /* TCS 2.1 */
  MAK_FUNC_0(kern_gmmrr_cr_ind,             GMMRR_CR_IND),
};


static const T_FUNC cgrlc_table[] = /* TCS 2.1 */
{ /* TCS 2.1 */
  MAK_FUNC_N(primitive_not_supported,       0                  ), /* TCS 2.1 */
  MAK_FUNC_N(primitive_not_supported,       0                  ), /* TCS 2.1 */
  MAK_FUNC_N(primitive_not_supported,       0                  ), /* TCS 2.1 */
  MAK_FUNC_N(primitive_not_supported,       0                  ), /* TCS 2.1 */
  MAK_FUNC_N(primitive_not_supported,       0                  ), /* TCS 2.1 */
  MAK_FUNC_N(primitive_not_supported,       0                  ), /* TCS 2.1 */
  MAK_FUNC_N(primitive_not_supported,       0                  ), /* TCS 2.1 */
  MAK_FUNC_N(primitive_not_supported,       0                  ), /* TCS 2.1 */
  MAK_FUNC_0(kern_cgrlc_status_ind,         CGRLC_STATUS_IND ), /* TCS 2.1 */
  MAK_FUNC_0(kern_cgrlc_test_mode_cnf,      CGRLC_TEST_MODE_CNF ), /* TCS 2.1 */
  MAK_FUNC_0(rdy_cgrlc_trigger_ind,         CGRLC_TRIGGER_IND ), /* TCS 2.1 */
  MAK_FUNC_0(rdy_cgrlc_standby_state_ind,   CGRLC_STANDBY_STATE_IND ), /* TCS 2.1 */
  MAK_FUNC_0(rdy_cgrlc_ready_state_ind,     CGRLC_READY_STATE_IND ), /* TCS 2.1 */
}; /* TCS 2.1 */


#ifndef GMM_TCS4
static const T_FUNC gmmsm_table[] =
{
  MAK_FUNC_0(kern_gmmsm_establish_req,      GMMSM_ESTABLISH_REQ),
  MAK_FUNC_S(tx_gmmsm_unitdata_req,         GMMSM_UNITDATA_REQ),
  MAK_FUNC_0(kern_gmmsm_sequence_res,       GMMSM_SEQUENCE_RES),
};
#else   
static const T_FUNC mmpm_table[] =
{
#ifdef REL99
  MAK_FUNC_0(kern_gmmsm_pdp_status_req,    MMPM_PDP_CONTEXT_STATUS_REQ),
#else
  MAK_FUNC_0(primitive_not_supported,      MMPM_PDP_CONTEXT_STATUS_REQ),
#endif
  MAK_FUNC_N(primitive_not_supported,      0),
  MAK_FUNC_N(primitive_not_supported,      0),
  MAK_FUNC_0(kern_gmmsm_sequence_res,      MMPM_SEQUENCE_RES),
  MAK_FUNC_S(tx_gmmsm_unitdata_req,        MMPM_UNITDATA_REQ),
};

#endif /* #ifndef GMM_TCS4 */

static const T_FUNC gmmsms_table[] =
{
  MAK_FUNC_0(kern_gmmsms_reg_state_req,     GMMSMS_REG_STATE_REQ)
};

LOCAL const T_FUNC sim_table[] = {
  MAK_FUNC_0 (primitive_not_supported      , SIM_READ_CNF           ), /* 0x00 */
  MAK_FUNC_0 (primitive_not_supported      , SIM_UPDATE_CNF         ),
  MAK_FUNC_0 (primitive_not_supported      , SIM_READ_RECORD_CNF    ),
  MAK_FUNC_N (primitive_not_supported      , 0                      ),
  MAK_FUNC_0 (primitive_not_supported      , SIM_UPDATE_RECORD_CNF  ),
  MAK_FUNC_N (primitive_not_supported      , 0                      ),
  MAK_FUNC_N (primitive_not_supported      , 0                      ),
  MAK_FUNC_N (primitive_not_supported      , 0                      ),
  MAK_FUNC_0 (primitive_not_supported      , SIM_INCREMENT_CNF      ),
  MAK_FUNC_0 (primitive_not_supported      , SIM_VERIFY_PIN_CNF     ),
  MAK_FUNC_0 (primitive_not_supported      , SIM_CHANGE_PIN_CNF     ),
  MAK_FUNC_0 (primitive_not_supported      , SIM_DISABLE_PIN_CNF    ),
  MAK_FUNC_0 (primitive_not_supported      , SIM_ENABLE_PIN_CNF     ),
  MAK_FUNC_0 (primitive_not_supported      , SIM_UNBLOCK_CNF        ),
  MAK_FUNC_0 (kern_sim_authentication_cnf       , SIM_AUTHENTICATION_CNF ),
  MAK_FUNC_0 (primitive_not_supported      , SIM_MMI_INSERT_IND     ),
  MAK_FUNC_0 (primitive_not_supported      , SIM_MM_INSERT_IND   ),  
  MAK_FUNC_0 (kern_sim_remove_ind    , SIM_REMOVE_IND         ),
  MAK_FUNC_0 (primitive_not_supported      , SIM_SYNC_CNF           ),
  MAK_FUNC_0 (primitive_not_supported      , SIM_ACTIVATE_CNF       ),
  MAK_FUNC_0 (primitive_not_supported      , SIM_SMS_INSERT_IND     ), /* 0x14 */
  MAK_FUNC_0 (primitive_not_supported      , SIM_TOOLKIT_IND        ), /* 0x15 */
  MAK_FUNC_0 (primitive_not_supported      , SIM_TOOLKIT_CNF        ), /* 0x16 */
  MAK_FUNC_0 (primitive_not_supported      , SIM_ACTIVATE_IND       ), /* 0x17 */
  MAK_FUNC_0 (primitive_not_supported      , SIM_MM_INFO_IND        ), /* 0x18 GMM GlumPs*/
  MAK_FUNC_0 (primitive_not_supported      , SIM_ACCESS_CNF         ), /* 0x19 */
  MAK_FUNC_0 (primitive_not_supported   , SIM_FILE_UPDATE_IND    ),  /* 0x1a */
  MAK_FUNC_0 (kern_sim_gmm_insert_ind  , SIM_GMM_INSERT_IND   ),  /* 0x1b */
};

static const T_FUNC ll_table[] =
{
  MAK_FUNC_0(primitive_not_supported,       LL_RESET_IND),
  MAK_FUNC_S(primitive_not_supported,       LL_ESTABLISH_CNF),
  MAK_FUNC_S(primitive_not_supported,       LL_ESTABLISH_IND),
  MAK_FUNC_0(primitive_not_supported,       LL_RELEASE_CNF),
  MAK_FUNC_0(primitive_not_supported,       LL_RELEASE_IND),
  MAK_FUNC_S(primitive_not_supported,       LL_XID_CNF),
  MAK_FUNC_S(primitive_not_supported,       LL_XID_IND),
  MAK_FUNC_0(primitive_not_supported,       LL_READY_IND),
  MAK_FUNC_0(primitive_not_supported,       LL_UNITREADY_IND),
  MAK_FUNC_0(primitive_not_supported,       LL_DATA_CNF),
  MAK_FUNC_S(primitive_not_supported,       LL_DATA_IND),
  MAK_FUNC_S(rx_ll_unitdata_ind,            LL_UNITDATA_IND)
};

static const T_FUNC llgmm_table[] =
{
  MAK_FUNC_0(kern_llgmm_status_ind,         LLGMM_STATUS_IND),
  MAK_FUNC_0(kern_llgmm_tlli_ind,         LLGMM_TLLI_IND)
};

static const T_FUNC mmgmm_table[] =
{
  MAK_FUNC_0(sync_mmgmm_reg_cnf,            MMGMM_REG_CNF),
  MAK_FUNC_0(sync_mmgmm_reg_rej,            MMGMM_REG_REJ),
  MAK_FUNC_0(sync_mmgmm_nreg_ind,           MMGMM_NREG_IND),
  MAK_FUNC_0(kern_mmgmm_nreg_cnf,           MMGMM_NREG_CNF),
  MAK_FUNC_0(kern_mmgmm_plmn_ind,           MMGMM_PLMN_IND),
  MAK_FUNC_0(kern_mmgmm_auth_rej_ind,       MMGMM_AUTH_REJ_IND),
  MAK_FUNC_0(kern_mmgmm_cm_establish_ind,   MMGMM_CM_ESTABLISH_IND),
  MAK_FUNC_0(kern_mmgmm_cm_release_ind,     MMGMM_CM_RELEASE_IND),
  MAK_FUNC_0(sync_mmgmm_activate_ind,       MMGMM_ACTIVATE_IND),
  MAK_FUNC_0(kern_mmgmm_t3212_val_ind,      MMGMM_T3212_VAL_IND),
  MAK_FUNC_0(kern_mmgmm_info_ind,           MMGMM_INFO_IND),
  MAK_FUNC_0(kern_mmgmm_cm_emergency_ind,   MMGMM_CM_EMERGENCY_IND),
  MAK_FUNC_0(kern_mmgmm_lup_accept_ind,     MMGMM_LUP_ACCEPT_IND),
  MAK_FUNC_0(kern_mmgmm_lup_needed_ind,     MMGMM_LUP_NEEDED_IND),
  MAK_FUNC_0(kern_mmgmm_ciphering_ind,      MMGMM_CIPHERING_IND),
  MAK_FUNC_0(kern_mmgmm_tmsi_ind,           MMGMM_TMSI_IND),
  MAK_FUNC_0(kern_mmgmm_ahplmn_ind,         MMGMM_AHPLMN_IND)
};


/*==== PRIVATE FUNCTIONS ====================================================*/

/*
+------------------------------------------------------------------------------
|  Function    : primitive_not_supported
+------------------------------------------------------------------------------
|  Description  :  This function handles unsupported primitives.
|
|  Parameters  :  -
|
|  Return      :  -
|
+------------------------------------------------------------------------------
*/
LOCAL void primitive_not_supported (void *data)
{
  TRACE_FUNCTION ("primitive_not_supported");

  PFREE (data);
}


/*==== PUBLIC FUNCTIONS =====================================================*/

/*
+------------------------------------------------------------------------------
|  Function    : pei_primitive
+------------------------------------------------------------------------------
|  Description  :  This function is called by the frame when a primitive is
|                received and needs to be processed.
|
|                             |
|                          GMMREG
|                       |     |     |     |
|                     GMMSM   |   GMMSM GMMAA             UPLINK
|                       |     |     |     |
|                   +---v-----v-----v-----v----+
|                   |                          |
|         MMGMM ---->           GMM            |
|                   |                          |
|                   +---^--------^--------^----+
|                       |        |        |
|                       |        |      LLGMM            DOWNLINK
|                       |      GMMRR      |
|                       |        |
|                     SIM
|                       |
|
|
|  Parameters  :  prim      - Pointer to the received primitive
|
|  Return      :  PEI_OK    - function succeeded
|               PEI_ERROR - function failed
|
+------------------------------------------------------------------------------
*/
LOCAL SHORT pei_primitive (void * primptr)
{
  TRACE_FUNCTION ("pei_primitive");
#ifdef TRACE_FUNC
#ifdef IDENTATION
  gmm_data->deep=0;
#endif
#endif

  if (primptr NEQ NULL)
  {
    T_PRIM *prim  = (T_PRIM *)primptr;
    ULONG           opc = prim->custom.opc;
    USHORT           n;
    const T_FUNC    *table;

    /*
     * This must be called for Partition Pool supervision. Will be replaced
     * by another macro some time.
     */
    VSI_PPM_REC (&prim->custom, __FILE__, __LINE__);

    GMM_TRACE_GMM_DATA(GMM_DEBUG_PRINT_MASK_STATE);
    PTRACE_IN (opc);

    switch (SAP_NR(opc))
    {
      case GMMREG_UL:
        table = gmmreg_table;
        n = TAB_SIZE (gmmreg_table);
        break;
      case GMMRR_DL:
        table = gmmrr_table;
        n = TAB_SIZE (gmmrr_table);
        break;      
      case SAP_NR(CGRLC_DL):  /* TCS 2.1 */
        table = cgrlc_table;  /* TCS 2.1 */
        n = TAB_SIZE (cgrlc_table); /* TCS 2.1 */
        break; /* TCS 2.1 */
#ifndef GMM_TCS4
      case GMMSM_UL:
        table = gmmsm_table;
        n = TAB_SIZE (gmmsm_table);
        break;
#else
      case SAP_NR(MMPM_UL):
        table = mmpm_table;
        n = TAB_SIZE (mmpm_table);
        break;
#endif
      case GMMSMS_UL:
        table = gmmsms_table;
        n = TAB_SIZE (gmmsms_table);
        break;
      case SAP_NR(SIM_UL):/*lint !e778 (Info -- Constant expression evaluates to 0 in operation '&') */
        table = sim_table;
        n = TAB_SIZE (sim_table);
        break;
      case LL_DL:
        table = ll_table;
        n = TAB_SIZE (ll_table);
        break;
      case LLGMM_DL:
        table = llgmm_table;
        n = TAB_SIZE (llgmm_table);
        break;
      case MMGMM_DL:
        table = mmgmm_table;
        n = TAB_SIZE (mmgmm_table);
        break;
#ifdef FF_EM_MODE
      case EM_Ul:
        table = em_ul_table;
        n = TAB_SIZE (em_ul_table);
        break;
      case EM_Dl:
        table = em_dl_table;
        n = TAB_SIZE (em_dl_table);
        break;
#endif /* FF_EM_MODE */
      default:
        TRACE_ERROR("pei_primitive. Unknown SAP");
        table = NULL;
        n = 0;
        break;
    }

    if (table != NULL)
    {
      if (PRIM_NR(opc) < n)
      {
        table += PRIM_NR(opc);
#ifdef PALLOC_TRANSITION
        P_SDU(prim) = table->soff ? (T_sdu*) (((char*)&prim->data) + table->soff) : 0;
#ifndef NO_COPY_ROUTING
        P_LEN(prim) = table->size + sizeof (T_PRIM_HEADER);
#endif /* NO_COPY_ROUTING */
#endif /* PALLOC_TRANSITION */

        /*******************************************************
         * SAVE prim
         *******************************************************/

        if( gmm_pei_handle_save( prim, &save_queue, save_tab, table->func )
          ==  PEI_SIGNAL_NOT_SAVED )
        {
          UBYTE old_kern_state = gmm_data->kern.state;
          /*******************************************************
           * SAVE prim
           *******************************************************/

          JUMP (table->func) (P2D(prim));
          
          /*******************************************************
           * SAVE prim
           *******************************************************/
          if( gmm_data->kern.state != old_kern_state )
          {
            gmm_pei_handle_queue(&save_queue, save_tab);
          }
        } 
        else
        {
          TRACE_2_INFO ("S:%2d SAVE of Primitive 0x%x", gmm_data->kern.state, opc );
        }



        /*******************************************************
         * End SAVE prim
         *******************************************************/
        
      }
      else
      {
        primitive_not_supported (P2D(prim));
      }
      return PEI_OK;
    }

    /*
     * primitive is not a GSM primitive - forward it to the environment
     */
    if (opc & SYS_MASK)
      vsi_c_primitive (VSI_CALLER prim);
    else
    {
      PFREE (P2D(prim));
      return PEI_ERROR;
    }
  }
  return PEI_OK;
}


/*
+------------------------------------------------------------------------------
|  Function    : pei_init
+------------------------------------------------------------------------------
|  Description  : This function is called by the frame. It is used to initialise
|               the entitiy.
|
|  Parameters  :  handle            - task handle
|
|  Return      :  PEI_OK            - entity initialised
|               PEI_ERROR         - entity not (yet) initialised
|
+------------------------------------------------------------------------------
*/
LOCAL SHORT pei_init (T_HANDLE handle)
{
  TRACE_FUNCTION ("pei_init");

  /*
   * Initialize task handle
   */
  GMM_handle = handle;

  /*
   * Open communication channels
   */
  if (hCommSMS < VSI_OK)
  {
    if ((hCommSMS = vsi_c_open (VSI_CALLER SMS_NAME)) < VSI_OK)
      return PEI_ERROR;
  }
  if (hCommSM < VSI_OK)
  {
    if ((hCommSM = vsi_c_open (VSI_CALLER SM_NAME)) < VSI_OK)
      return PEI_ERROR;
  }
  if (hCommGRLC < VSI_OK) /* TCS 2.1 */
  { /* TCS 2.1 */
    if ((hCommGRLC = vsi_c_open (VSI_CALLER GRLC_NAME)) < VSI_OK) /* TCS 2.1 */
      return PEI_ERROR; /* TCS 2.1 */
  } /* TCS 2.1 */

  if (hCommGRR < VSI_OK)
  {
    if ((hCommGRR = vsi_c_open (VSI_CALLER GRR_NAME)) < VSI_OK)
      return PEI_ERROR;
  }
  if (hCommLLC < VSI_OK)
  {
    if ((hCommLLC = vsi_c_open (VSI_CALLER LLC_NAME)) < VSI_OK)
      return PEI_ERROR;
  }
  if (hCommSIM < VSI_OK)
  {
    if ((hCommSIM = vsi_c_open (VSI_CALLER SIM_NAME)) < VSI_OK)
      return PEI_ERROR;
  }
  if (hCommMM < VSI_OK)
  {
    if ((hCommMM = vsi_c_open (VSI_CALLER MM_NAME)) < VSI_OK)
      return PEI_ERROR;
  }
  if (hCommMMI < VSI_OK)
  {
    if ((hCommMMI = vsi_c_open (VSI_CALLER ACI_NAME)) < VSI_OK) /* TCS 2.1 */
      return PEI_ERROR;
  }
  if (hCommGMM < VSI_OK)
  {
    if ((hCommGMM = vsi_c_open (VSI_CALLER GMM_NAME)) < VSI_OK)
      return PEI_ERROR;
  }
#ifdef GMM_TCS4
  if (hCommUPM < VSI_OK)
  {
    if ((hCommUPM = vsi_c_open (VSI_CALLER UPM_NAME)) < VSI_OK)
      return PEI_ERROR;
  }
#endif


  /*
   * Initialize global pointer llc_data. This is required to access all
   * entity data.
   */
  gmm_data = &gmm_data_base;

  /*
   * Initialite ccd
   */
  ccd_init ();

#ifdef FF_EM_MODE
  em_init_gmm_event_trace();
#endif /* FF_EM_MODE */


  /*
   * Initialize entity data (call init function of every service)
   */

  kern_init();
  rxgmm_init();
  txgmm_init();
  rdy_init();
  sync_gmm_init();

  GMM_TRACE_GMM_DATA(GMM_DEBUG_PRINT_MASK_FULL);

  return (PEI_OK);
}

/*
+------------------------------------------------------------------------------
|  Function    : pei_timeout
+------------------------------------------------------------------------------
|  Description  : This function is called by the frame when a timer has expired.
|
|  Parameters  :  index             - timer index
|
|  Return      :  PEI_OK            - timeout processed
|               PEI_ERROR         - timeout not processed
|
+------------------------------------------------------------------------------
*/
LOCAL SHORT pei_timeout (USHORT index)
{
  UBYTE      old_kern_state;
  TRACE_FUNCTION ("pei_timeout");

  old_kern_state = gmm_data->kern.state;
  /*
   * Process timeout
   */
  switch (index)
  {
    case kern_T3302:
      /*
       * T3302 expired.
       */
      kern_t3302 ();
      break;
    case kern_T3310:
      /*
       * T3310 expired.
       */
      kern_t3310 ();
      break;
    case kern_T3311:
      /*
       * T3311 expired.
       */
      kern_t3311 ();
      break;
    case kern_T3321:
      /*
       * T3321 expired.
       */
      kern_t3321 ();
      break;
    case rdy_T3312:
      /*
       * T3312 expired.
       */
      kern_t3312 ();
      break;
    case kern_TPOWER_OFF:
      kern_tpower_off();
      break;
    case kern_TLOCAL_DETACH:
      kern_tlocal_detach();
      break;
    case sync_TSYNC:
      sync_tsync();
      break;
    default:
      TRACE_ERROR("Unknown Timeout");
      return PEI_ERROR;
  }

  if( gmm_data->kern.state != old_kern_state )
  {
    gmm_pei_handle_queue(  &save_queue, save_tab);
  }
  return PEI_OK;
}

/*
+------------------------------------------------------------------------------
|  Function    : pei_signal
+------------------------------------------------------------------------------
|  Description  : This function is called by the frame when a signal has been
|               received.
|
|  Parameters  :  opc               - signal operation code
|               *data             - pointer to primitive
|
|  Return      :  PEI_OK            - signal processed
|               PEI_ERROR         - signal not processed
|
+------------------------------------------------------------------------------
*/
LOCAL SHORT pei_signal (ULONG opc, void *data)
{
  TRACE_FUNCTION ("pei_signal");

  /*
   * Process signal
   */
  switch (opc)
  {
    default:
      TRACE_ERROR("Unknown Signal OPC");
      break;
  }/*lint !e764 (Info -- switch statement does not have a case) */

  return PEI_OK;
}

/*
+------------------------------------------------------------------------------
|  Function    : pei_exit
+------------------------------------------------------------------------------
|  Description  : This function is called by the frame when the entity is
|               terminated. All open resources are freed.
|
|  Parameters  :  -
|
|  Return      :  PEI_OK            - exit sucessful
|               PEI_ERROR         - exit not sueccessful
|
+------------------------------------------------------------------------------
*/
LOCAL SHORT pei_exit (void)
{
  TRACE_FUNCTION ("pei_exit");

  /*
   * Close communication channels
   */
  vsi_c_close (VSI_CALLER hCommSMS);
  hCommSMS = VSI_ERROR;

  vsi_c_close (VSI_CALLER hCommSM);
  hCommSM = VSI_ERROR;

  vsi_c_close (VSI_CALLER hCommGRLC); /* TCS 2.1 */
  hCommGRLC = VSI_ERROR; /* TCS 2.1 */

  vsi_c_close (VSI_CALLER hCommGRR);
  hCommGRR = VSI_ERROR;

  vsi_c_close (VSI_CALLER hCommLLC);
  hCommLLC = VSI_ERROR;

  vsi_c_close (VSI_CALLER hCommSIM);
  hCommSIM = VSI_ERROR;

  vsi_c_close (VSI_CALLER hCommMM);
  hCommMM = VSI_ERROR;

  vsi_c_close (VSI_CALLER hCommMMI);
  hCommMMI = VSI_ERROR;

  vsi_c_close (VSI_CALLER hCommGMM);
  hCommGMM = VSI_ERROR;

  /*
   * delete queues
   */
  gmm_pei_delete_queue(&save_queue);
  
  /*
   * close ccd
   */
  ccd_exit ();

  return PEI_OK;
}

/*
+------------------------------------------------------------------------------
|  Function    : pei_run
+------------------------------------------------------------------------------
|  Description  : This function is called by the frame when entering the main
|               loop. This fucntion is only required in the active variant.
|
|               This function is not used.
|
|  Parameters  :  handle            - Communication handle
|
|  Return      :  PEI_OK            - sucessful
|               PEI_ERROR         - not successful
|
+------------------------------------------------------------------------------
*/
LOCAL SHORT pei_run (T_HANDLE TaskHandle, T_HANDLE ComHandle )
{
  return PEI_OK;
}


/*
+------------------------------------------------------------------------------
|  Function    : gmm_tok_key
+------------------------------------------------------------------------------
|  Description  : This function is used by pei_config in windows test
|                 environment for string tokenizing for CHECK STATE command, e.g.
|                 COMMAND("GMM CONFIG CHECK_STATE=<REG_NORMAL_SERVICE>");
|
|
|  Parameters  :  
|
|  Return      :  
|               
|
+------------------------------------------------------------------------------
*/
#ifndef _TARGET_
typedef struct KW_STATE_DATA
{
   char   keyword[60];
   SHORT  code;
} KW_STATE_DATA;

LOCAL SHORT gmm_tok_key (KW_STATE_DATA * keytab, char * keyword)
{
  /*
   * Empty string terminates
   */
  while (keytab->keyword[0])
  {
    if (strcmp (keytab->keyword, keyword ) == 0)
      return (keytab->code);
    keytab++;
  }

  return (TOK_NOT_FOUND);
}
#endif

/*
+------------------------------------------------------------------------------
|  Function    : pei_config
+------------------------------------------------------------------------------
|  Description  : This function is called by the frame when a primitive is
|               received indicating dynamic configuration.
|
|               This function is not used in this entity.
|
|  Parameters  :  handle            - Communication handle
|
|  Return      :  PEI_OK            - sucessful
|               PEI_ERROR         - not successful
|
+------------------------------------------------------------------------------
*/
LOCAL SHORT pei_config (char *inString)
{
  TRACE_FUNCTION ("pei_config");
  TRACE_FUNCTION (inString);

#ifndef NCONFIG
   /*
   * Parse next keyword and number of variables
   */
    if(!strcmp(inString,"PERIODIC_LAU"))
    {
      PALLOC (mmgmm_lup_needed_ind, MMGMM_LUP_NEEDED_IND);
      mmgmm_lup_needed_ind->reason = MMGMM_T3212; /* TCS 2.1 */
      TRACE_EVENT(  "Info: Periodic LAU requested by MM");
      PSEND (hCommGMM, mmgmm_lup_needed_ind);
    }
    else if(!strcmp(inString,"PERIODIC_RAU"))
    {
      kern_t3312();
    }
    else if(!strcmp(inString,"REATTACH"))
    {
      PALLOC (gmmrr_page_ind,  GMMRR_PAGE_IND);
        gmmrr_page_ind->page_id=GMMRR_IMSI;
      PSEND (hCommGMM, gmmrr_page_ind);
    }
    else if(!strcmp(inString,"CELL"))
    {
      PALLOC (gmmrr_cell_ind,  GMMRR_CELL_IND);
      gmmrr_cell_ind->cell_info.service_state=GMMRR_SERVICE_FULL;
      gmmrr_cell_ind->cell_info.cell_env.rai.lac=0x2506;
      gmmrr_cell_ind->cell_info.cell_env.rai.rac=0x71;
      gmmrr_cell_ind->cell_info.net_mode=gmm_data->config.nmo;
      PSEND (hCommGMM, gmmrr_cell_ind);
    }
    else if(!strcmp(inString,"NMO_I"))
    {
      gmm_data->config.nmo = GMMRR_NET_MODE_I;
    }
    else if(!strcmp(inString,"NMO_II"))
    {
      gmm_data->config.nmo = GMMRR_NET_MODE_II;
    }
    else if(!strcmp(inString,"NMO_III"))
    {
      gmm_data->config.nmo = GMMRR_NET_MODE_III;
    }
    else if(!strcmp(inString,"GMM_INFO"))
    {
      PALLOC (gmmreg_info_ind,  GMMREG_INFO_IND);
      PSEND (hCommMMI, gmmreg_info_ind);
    }

    else if(!strcmp(inString,"RESET_SIM"))
    {
      gmm_data->sim_gprs_invalid = TRUE;
      kern_sim_del_locigprs ();
      kern_sim_gmm_update();
      kern_mm_auth_rej();
      {
        PALLOC (sim_update_req, SIM_UPDATE_REQ);

        sim_update_req->source         = SRC_GMM;
        /* req_id is not filled since response is not handled */

        sim_update_req->v_path_info    = FALSE;

        sim_update_req->datafield      = SIM_FPLMN;
        sim_update_req->length         = 12;
        memset (sim_update_req->trans_data,0xff,12);
        sim_update_req->offset         = 0;

        PSEND (hCommSIM, sim_update_req);
      }
      {
        PALLOC (sim_update_req, SIM_UPDATE_REQ);

        sim_update_req->source         = SRC_GMM;

        /* req_id is not filled since response is not handled */        
        sim_update_req->v_path_info    = FALSE;

        sim_update_req->datafield      = SIM_LOCI;
        sim_update_req->length         = 11;
        memset (sim_update_req->trans_data,0xff,8);
        sim_update_req->trans_data[8]=0xfe;
        sim_update_req->trans_data[9]=0x0;
        sim_update_req->trans_data[10]=GU2_NOT_UPDATED;
        sim_update_req->offset         = 0;

        PSEND (hCommSIM, sim_update_req);
      }
    }
    else if(!strcmp(inString,"ANITE"))
    {
      gmm_data->anite = TRUE;
    }
    else if(!strcmp(inString,"CIPHER_OFF"))
    {
      gmm_data->config.cipher_on =   0x00;
      TRACE_EVENT ("ciphering switched OFF");
    }
    else if(!strcmp(inString,"CIPHER_ON"))
    {
      gmm_data->config.cipher_on |= 0x01;
    }
    else if(!strcmp(inString,"PREUSE_OFF"))
    {
      gmm_data->config.preuse_off =    TRUE;
    }

#ifdef REL99
    else if(!strcmp(inString,"CL_SGSN_REL_98_OR_OLDER")) /* TCS 4.0 */
    { /* TCS 4.0 */
      /*gmm_data->config.sgsnr_flag =    R_98NW; */
      cl_nwrl_set_sgsn_release(PS_SGSN_98_OLDER); /*CL function*/ /* TCS 4.0 */
    } /* TCS 4.0 */
    else if(!strcmp(inString,"CL_SGSN_REL_99_ONWARDS")) /* TCS 4.0 */
    { /* TCS 4.0 */
      /* gmm_data->config.sgsnr_flag =    R_99NW; */
      cl_nwrl_set_sgsn_release(PS_SGSN_99_ONWARDS); /*CL function*/ /* TCS 4.0 */
    } /* TCS 4.0 */
    else if (!strcmp(inString,"CELL_NOTIFY_ON")) /* TCS 4.0 */
    { /* TCS 4.0 */
      gmm_data->config.cell_notification = FIRST_CELL_NOTIFY; /* TCS 4.0 */
    } /* TCS 4.0 */
    else if (!strcmp(inString, "CELL_NOTIFY_OFF")) /* TCS 4.0 */
    { /* TCS 4.0 */
      gmm_data->config.cell_notification = NO_CELL_NOTIFY; /* TCS 4.0 */
    } /* TCS 4.0 */
#endif

#ifdef _SIMULATION_    
    else if(!strcmp(inString, "NEXT_TIMEOUT"))
    {
      T_TIME old_value = 0;
      T_TIME new_value = 0;
      USHORT timer;
      USHORT timeout = TIMER_MAX;
    
      /* Find next timer to timeout */    
      
      for(timer = 0; timer < TIMER_MAX; timer++)
      {
        if ( VSI_OK == vsi_t_status ( GMM_handle ,timer, &new_value ))
        {
          if (new_value >0 )
          {
            if((old_value==0)||
               (new_value < old_value))
            {
              old_value = new_value;
              timeout = timer;        
            }
          }
        }
      }

      
      /* Force timeout */
      if(TIMER_MAX != timeout )
      {
        TRACE_1_INFO("gmm_pei_config - forcing timeout:%d", timeout);
        
        vsi_t_stop ( GMM_handle, timeout);       

        for(timer = 0; timer < TIMER_MAX ; timer++)
        {
          if ( VSI_OK == vsi_t_status ( GMM_handle ,timer, &new_value ))
          {
            if (timer!=timeout )
            {
              if (new_value>0 && new_value-old_value >0 )
              {
                vsi_t_start ( GMM_handle , timer, new_value-old_value ); 
              }
            }
          }
        }

        pei_timeout(timeout);
      }
    }
#endif

#define GMM_CONFIG_DRX               1
#define GMM_CONFIG_GEA            2

#ifndef _TARGET_
#define GMM_CONFIG_CHECK_STATE       3
#endif

#define GMM_DRX               "DRX"
#define GMM_GEA               "GEA"

#ifndef _TARGET_
#define GMM_CHECK_STATE       "CHECK_STATE"

#define GMM_NULL_NO_IMSI                      "NULL_NO_IMSI"
#define GMM_NULL_IMSI                         "NULL_IMSI"
#define GMM_DEREG_INITIATED                   "DEREG_INITIATED"  
#define GMM_DEREG_ATTEMPTING_TO_ATTACH        "DEREG_ATTEMPTING_TO_ATTACH"
#define GMM_DEREG_NO_CELL_AVAILABLE           "DEREG_NO_CELL_AVAILABLE"
#define GMM_DEREG_LIMITED_SERVICE             "DEREG_LIMITED_SERVICE"
#define GMM_DEREG_NO_IMSI                     "DEREG_NO_IMSI"
#define GMM_DEREG_PLMN_SEARCH                 "DEREG_PLMN_SEARCH"
#define GMM_DEREG_SUSPENDED                   "DEREG_SUSPENDED"

#define GMM_REG_INITIATED                     "REG_INITIATED"

#define GMM_REG_NO_CELL_AVAILABLE             "REG_NO_CELL_AVAILABLE"
#define GMM_REG_LIMITED_SERVICE               "REG_LIMITED_SERVICE"
#define GMM_REG_ATTEMPTING_TO_UPDATE_MM       "REG_ATTEMPTING_TO_UPDATE_MM  "
#define GMM_REG_ATTEMPTING_TO_UPDATE          "REG_ATTEMPTING_TO_UPDATE"
#define GMM_REG_RESUMING                      "REG_RESUMING"
#define GMM_REG_SUSPENDED                     "REG_SUSPENDED"
#define GMM_REG_NORMAL_SERVICE                "REG_NORMAL_SERVICE"

#define GMM_RAU_INITIATED                     "RAU_INITIATED"

#define GMM_RAU_WAIT_FOR_NPDU_LIST            "RAU_WAIT_FOR_NPDU_LIST"

#define GMM_REG_IMSI_DETACH_INITIATED         "REG_IMSI_DETACH_INITIATED"

#define GMM_DEREG_SUSPENDING                  "DEREG_SUSPENDING"
#define GMM_DEREG_RESUMING                    "DEREG_RESUMING"

#define GMM_REG_SUSPENDING                    "REG_SUSPENDING"
#define GMM_NULL_NO_IMSI_LIMITED_SERVICE_REQ     "NULL_NO_IMSI_LIMITED_SERVICE_REQ"
#define GMM_NULL_IMSI_LIMITED_SERVICE_REQ     "NULL_IMSI_LIMITED_SERVICE_REQ"
#define GMM_REG_TEST_MODE                     "REG_TEST_MODE"
#define GMM_NULL_PLMN_SEARCH                  "NULL_PLMN_SEARCH"
#define GMM_REG_TEST_MODE_NO_IMSI             "REG_TEST_MODE_NO_IMSI "

#endif
    
    {   
      LOCAL KW_DATA kwtab[] = 
      {
        GMM_DRX, GMM_CONFIG_DRX,
        GMM_GEA, GMM_CONFIG_GEA,
#ifndef _TARGET_
        GMM_CHECK_STATE, GMM_CONFIG_CHECK_STATE,
#endif
        "", 0  
      };
#ifndef _TARGET_
      LOCAL KW_STATE_DATA kw_state_tab[] = 
      {
         GMM_NULL_NO_IMSI                      ,KERN_GMM_NULL_NO_IMSI,
         GMM_NULL_IMSI                         ,KERN_GMM_NULL_IMSI,
         GMM_DEREG_INITIATED                   ,KERN_GMM_DEREG_INITIATED,  
         GMM_DEREG_ATTEMPTING_TO_ATTACH        ,KERN_GMM_DEREG_ATTEMPTING_TO_ATTACH,
         GMM_DEREG_NO_CELL_AVAILABLE           ,KERN_GMM_DEREG_NO_CELL_AVAILABLE,
         GMM_DEREG_LIMITED_SERVICE             ,KERN_GMM_DEREG_LIMITED_SERVICE,
         GMM_DEREG_NO_IMSI                     ,KERN_GMM_DEREG_NO_IMSI,
         GMM_DEREG_PLMN_SEARCH                 ,KERN_GMM_DEREG_PLMN_SEARCH,
         GMM_DEREG_SUSPENDED                   ,KERN_GMM_DEREG_SUSPENDED,

         GMM_REG_INITIATED                     ,KERN_GMM_REG_INITIATED,

         GMM_REG_NO_CELL_AVAILABLE             ,KERN_GMM_REG_NO_CELL_AVAILABLE,
         GMM_REG_LIMITED_SERVICE               ,KERN_GMM_REG_LIMITED_SERVICE,
         GMM_REG_ATTEMPTING_TO_UPDATE_MM       ,KERN_GMM_REG_ATTEMPTING_TO_UPDATE_MM  ,
         GMM_REG_ATTEMPTING_TO_UPDATE          ,KERN_GMM_REG_ATTEMPTING_TO_UPDATE,
         GMM_REG_RESUMING                      ,KERN_GMM_REG_RESUMING,
         GMM_REG_SUSPENDED                     ,KERN_GMM_REG_SUSPENDED,
         GMM_REG_NORMAL_SERVICE                ,KERN_GMM_REG_NORMAL_SERVICE,

         GMM_RAU_INITIATED                     ,KERN_GMM_RAU_INITIATED,

         GMM_RAU_WAIT_FOR_NPDU_LIST            ,KERN_GMM_RAU_WAIT_FOR_NPDU_LIST,

         GMM_REG_IMSI_DETACH_INITIATED         ,KERN_GMM_REG_IMSI_DETACH_INITIATED,

         GMM_DEREG_SUSPENDING                  ,KERN_GMM_DEREG_SUSPENDING,
         GMM_DEREG_RESUMING                    ,KERN_GMM_DEREG_RESUMING,
         GMM_REG_SUSPENDING                    ,KERN_GMM_REG_SUSPENDING,
         
         GMM_NULL_NO_IMSI_LIMITED_SERVICE_REQ     ,KERN_GMM_NULL_NO_IMSI_LIMITED_SERVICE_REQ,
         GMM_NULL_IMSI_LIMITED_SERVICE_REQ     ,KERN_GMM_NULL_IMSI_LIMITED_SERVICE_REQ,
         GMM_REG_TEST_MODE                     ,KERN_GMM_REG_TEST_MODE,
         GMM_NULL_PLMN_SEARCH                  ,KERN_GMM_NULL_PLMN_SEARCH,
         GMM_REG_TEST_MODE_NO_IMSI             ,KERN_GMM_REG_TEST_MODE_NO_IMSI ,
        "", 0  
      };

#endif
      SHORT valno;
      char *keyw;
      char *val[10];
      
      tok_init (inString);

      while((valno = tok_next(&keyw,val)) NEQ TOK_EOCS)
      {
        switch ((tok_key((KW_DATA *)kwtab, keyw)))
        {
          case GMM_CONFIG_DRX:
            if (3==valno)
            {
              TRACE_EVENT ("use of DRX <split_pg_cycle_code,split_on_ccch,non_drx_timer>");
              gmm_data->drx_parameter.split_pg_cycle_code = atoi (val[0]);
              gmm_data->drx_parameter.split_on_ccch       = atoi (val[1]);
              gmm_data->drx_parameter.non_drx_timer       = atoi (val[2]);
            }
            else
            {
              TRACE_ERROR("[PEI_CONFIG]: use CONFIG DRX <1,2,3>");
            }
            break;
          case GMM_CONFIG_GEA:
            if (1==valno)
            {
              TRACE_EVENT ("GEA=<bit_field of wanted GEA in dec>");
              
              gmm_data->config.cipher_on |= atoi (val[0]);
              TRACE_1_INFO("GEA2=%d", (gmm_data->config.cipher_on & 0x02)>0);
              TRACE_1_INFO("GEA3=%d", (gmm_data->config.cipher_on & 0x04)>0);
              TRACE_1_INFO("GEA4=%d", (gmm_data->config.cipher_on & 0x08)>0);
              TRACE_1_INFO("GEA5=%d", (gmm_data->config.cipher_on & 0x10)>0);
              TRACE_1_INFO("GEA6=%d", (gmm_data->config.cipher_on & 0x20)>0);
              TRACE_1_INFO("GEA7=%d", (gmm_data->config.cipher_on & 0x40)>0);
            }
            else
            {
              TRACE_ERROR("[PEI_CONFIG]: use CONFIG GEA=<0-255>//255 means all GEA wanted");
            }
            break;
#ifndef _TARGET_
          case GMM_CONFIG_CHECK_STATE:
            if(1==valno)
            {
              if (gmm_tok_key ((KW_STATE_DATA *)kw_state_tab, val[0])!=GET_STATE(KERN))
              {
                PALLOC ( cgrlc_status_ind, CGRLC_STATUS_IND ); /* TCS 2.1 */
                PSEND ( hCommGMM, cgrlc_status_ind ); /* TCS 2.1 */
                TRACE_1_OUT_PARA(   "neq %s", val[0]);
                TRACE_ERROR("state wrong");
              }
              else
              {
                TRACE_1_INFO("state %s ok",val[0]);
              }
            }
            else
            {
              TRACE_ERROR("[PEI_CONFIG]: USE CONFIG CHECK_STATE <state>");
            }
            break;
#endif            
          default:
            break;
        }
      }
    }
#endif
    return PEI_OK;
}

/*
+------------------------------------------------------------------------------
|  Function    : pei_monitor
+------------------------------------------------------------------------------
|  Description  : This function is called by the frame in case sudden entity
|               specific data is requested (e.g. entity Version).
|
|  Parameters  :  out_monitor       - return the address of the data to be
|                                   monitoredCommunication handle
|
|  Return      :  PEI_OK            - sucessful (address in out_monitor is valid)
|               PEI_ERROR         - not successful
|
+------------------------------------------------------------------------------
*/
LOCAL SHORT pei_monitor (void ** out_monitor)
{
  TRACE_FUNCTION ("pei_monitor");

  /*
   * Version = "0.S" (S = Step).
   */
  gmm_mon.version = "GMM 0.1";
  *out_monitor = &gmm_mon;

  return PEI_OK;
}

/*
+------------------------------------------------------------------------------
|  Function    : pei_create
+------------------------------------------------------------------------------
|  Description  :  This function is called by the frame when the process is
|                created.
|
|  Parameters  :  out_name          - Pointer to the buffer in which to locate
|                                   the name of this entity
|
|  Return      :  PEI_OK            - entity created successfuly
|               PEI_ERROR         - entity could not be created
|
+------------------------------------------------------------------------------
*/
GLOBAL SHORT pei_create (T_PEI_INFO **info)
{
static T_PEI_INFO pei_info =
              {
               "GMM",         /* name */
               {              /* pei-table */
                 pei_init,
                 pei_exit,
                 pei_primitive,
                 pei_timeout,
                 pei_signal,
                 pei_run,
                 pei_config,
                 pei_monitor
               },
               2048,          /* stack size */
               25,            /* queue entries */
               190,           /* priority (1->low, 255->high) */
               TIMER_MAX,     /* number of timers */
               0x03|PRIM_NO_SUSPEND /* flags: bit 0   active(0) body/passive(1) */
              };              /*        bit 1   com by copy(0)/reference(1) */


  TRACE_FUNCTION ("pei_create");

  /*
   * Close Resources if open
   */
  if (first_access)
    first_access = FALSE;
  else
    pei_exit();

  /*
   * Export startup configuration data
   */
  *info = &pei_info;

  return (PEI_OK);
}

/*
+------------------------------------------------------------------------------
| Function    : gmm_pei_handle_queue
+------------------------------------------------------------------------------
| Description : This function searches for the first stored primitive, which
|               must not be saved further in current state.
|
| Parameters  : state     - current state of the instance
|               queue     - PtrPtr to the hanger of the instance save queue
|               table     - Ptr to the state/signals-to-save table
|               inst_data - Ptr to instance data
+------------------------------------------------------------------------------
*/
LOCAL void gmm_pei_handle_queue( T_SAVE_QUEUE** queue,
                                      const T_SAVE_TAB* table)
{
  /* search for a T_SAVE_TAB entry which fits to current state */
  if( *queue )
    while( table->state != PEI_END_SAVETAB && table->state != gmm_data->kern.state )
      table++;

  while( *queue )
  {
    T_SAVE_QUEUE* current = *queue;
    ULONG  opc            = ((T_PRIM_HEADER*)current->prim)->opc;
    USHORT n              = 0;

    if( table->state != PEI_END_SAVETAB )
    {
      /* search for this signals in savelist of this state */
      while( n < GMM_MAX_SIGNALS_SAVED && table->signal[n] &&
             table->signal[n] != opc                       )
        n++;
    }

    if( table->signal[n] != opc )
    {
      TRACE_1_INFO ("Pimitive 0x%x send from SAVE queue",
                    ((T_PRIM_HEADER*)current->prim)->opc );
      /* this signal must not further be stored -> handle it now */
      /* first remove it from save queue, because of recursion!! */
      *queue = current->next;
      gmm_pei_handle_prim(current->func, current->prim );
      /* and free memory after last use */
      MFREE( current );

      /* recursion! handle at maximum one prim at once -> job done */
      return;
    }

    /* goto next queued signal */
    queue = &((*queue)->next);
  }
}
/*
+------------------------------------------------------------------------------
| Function    : gmm_pei_delete_queue
+------------------------------------------------------------------------------
| Description : This function removes all stored primitives from the queue and
|               frees the memory.
|
| Parameters  : queue    - PtrPtr to the hanger of the instance save queue
+------------------------------------------------------------------------------
*/
GLOBAL void gmm_pei_delete_queue( T_SAVE_QUEUE** queue )
{
  while( *queue )
  {
    T_SAVE_QUEUE* current = *queue;


    TRACE_1_INFO( "Pimitive 0x%x deleted from SAVE queue",
                                         ((T_PRIM_HEADER*)current->prim)->opc );

    *queue = current->next;
    PFREE( P2D(current->prim) );
    MFREE( current );
  }
}
/*
+------------------------------------------------------------------------------
| Function    : gmm_pei_handle_prim
+------------------------------------------------------------------------------
| Description : This function calls the handle function of the signal
|
| Parameters  : func       - Function, which should handle the signal
|               inst_data  - Ptr to instance data
|               *prim      - Ptr to primitive
+------------------------------------------------------------------------------
*/
LOCAL void gmm_pei_handle_prim( T_VOID_FUNC func, void* prim )
{
  JUMP(func)( P2D(prim));

  gmm_pei_handle_queue(   &save_queue, save_tab);
}
/*
+------------------------------------------------------------------------------
| Function    : gmm_pei_handle_save
+------------------------------------------------------------------------------
| Description : This function saves the prim pointer to the save queue, if
|               this is necessary in current state.
|
| Parameters  : *prim    - Ptr to primitive
|               state    - Current state of the instance
|               queue    - PtrPtr to the hanger of the instance save queue
|               table    - Ptr to the state/signals-to-save table
|               func     - Function, which should handle the signal
|
| Returns     : PEI_SIGNAL_SAVED      - if signal is stored to save queue
|               PEI_SIGNAL_NOT_SAVED  - else
+------------------------------------------------------------------------------
*/
LOCAL BOOL gmm_pei_handle_save( void* prim, T_SAVE_QUEUE** queue,
                                     const T_SAVE_TAB* table, T_VOID_FUNC func  )
{
  ULONG opc = ((T_PRIM_HEADER*)prim)->opc;

  while( table->state != PEI_END_SAVETAB )
  {
    if( table->state == gmm_data->kern.state )
    {
      UBYTE n = 0;

      /* check for signals to save */
      while( n < GMM_MAX_SIGNALS_SAVED && table->signal[n] )
      {
        if( table->signal[n] == opc )
        {
          /* store signal to end of save queue */
          while( *queue )
            queue = &((*queue)->next);

          MALLOC( *queue, sizeof(T_SAVE_QUEUE) );

          if( *queue )
          {
            (*queue)->prim = prim;
            (*queue)->func = func;
            (*queue)->next = NULL;
          }
          else
          {
            TRACE_ERROR( "Out of memory in mm_pei_handle_save()" );
            return PEI_SIGNAL_NOT_SAVED;
          }

          return PEI_SIGNAL_SAVED;
        }

        /* goto next signal */
        n++;
      }

      break;
    }

    /* gote next entry */
    table++;
  }

  return PEI_SIGNAL_NOT_SAVED;
}

/*==== END OF FILE ==========================================================*/