view src/g23m-gsm/mm/mm_tim.c @ 303:f76436d19a7a default tip

!GPRS config: fix long-standing AT+COPS chance hanging bug There has been a long-standing bug in FreeCalypso going back years: sometimes in the AT command bring-up sequence of an ACI-only MS, the AT+COPS command would produce only a power scan followed by cessation of protocol stack activity (only L1 ADC traces), instead of the expected network search sequence. This behaviour was seen in different FC firmware versions going back to Citrine, and seemed to follow some law of chance, not reliably repeatable. This bug has been tracked down and found to be specific to !GPRS configuration, stemming from our TCS2/TCS3 hybrid and reconstruction of !GPRS support that was bitrotten in TCS3.2/LoCosto version. ACI module psa_mms.c, needed only for !GPRS, was missing in the TCS3 version and had to be pulled from TCS2 - but as it turns out, there is a new field in the MMR_REG_REQ primitive that needs to be set correctly, and that psa_mms.c module is the place where this initialization needed to be added.
author Mychaela Falconia <falcon@freecalypso.org>
date Thu, 08 Jun 2023 08:23:37 +0000
parents fa8dc04885d8
children
line wrap: on
line source

/* 
+----------------------------------------------------------------------------- 
|  Project :  GSM-PS (8410)
|  Modul   :  MM_TIM
+----------------------------------------------------------------------------- 
|  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 defines the timer handling functions
|             for the component MM of the mobile station
+----------------------------------------------------------------------------- 
*/ 

#ifndef MM_TIM_C
#define MM_TIM_C

#define ENTITY_MM

/*==== INCLUDES ===================================================*/
#if defined (NEW_FRAME)

#include <string.h>
#include <stdlib.h>
#include <stddef.h>
#include "typedefs.h"
#include "pcm.h"
#include "pconst.cdg"
#include "mconst.cdg"
#include "message.h"
#include "ccdapi.h"
#include "vsi.h"
#include "custom.h"
#include "gsm.h"
#include "prim.h"
#include "cnf_mm.h"
#include "mon_mm.h"
#include "pei.h"
#include "tok.h"
#include "mm.h"

#else

#include <string.h>
#include <stdlib.h>
#include <stddef.h>
#include "stddefs.h"
#include "pcm.h"
#include "pconst.cdg"
#include "mconst.cdg"
#include "message.h"
#include "ccdapi.h"
#include "custom.h"
#include "gsm.h"
#include "prim.h"
#include "cnf_mm.h"
#include "mon_mm.h"
#include "vsi.h"
#include "pei.h"
#include "tok.h"
#include "mm.h"

#endif
/*==== EXPORT =====================================================*/

/*==== PRIVAT =====================================================*/

/*==== VARIABLES ==================================================*/

#if defined (OPTION_TIMER)
LOCAL T_TIMER_CONFIG       config_table[NUM_OF_MM_TIMERS];
#endif


/*==== FUNCTIONS ==================================================*/
#if defined (OPTION_TIMER)
/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)         MODULE  : MM_TIM                   |
| STATE   : code                  ROUTINE : tim_init_timer           |
+--------------------------------------------------------------------+

  PURPOSE : Initialise Time-out FIFO and configuration data.

*/


GLOBAL BOOL tim_init_timer (void)
{
  USHORT i;

  TRACE_FUNCTION ("tim_init_timer()");

  for (i = 0; i < NUM_OF_MM_TIMERS; i++)
  {
    config_table[i].t_mode = TIMER_RESET;
    config_table[i].t_val  = 0L;
  }

  return TRUE;
}
#endif /* #if defined (OPTION_TIMER) */

#if defined (OPTION_TIMER)

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

  PURPOSE : Configure Timer

*/

GLOBAL void tim_config_timer (UBYTE t_num, UBYTE t_mod, ULONG t_val)
{
  TRACE_FUNCTION ("tim_config_timer()");

  assert (t_num < NUM_OF_MM_TIMERS);

  if (t_num < NUM_OF_MM_TIMERS)
  {
    config_table[t_num].t_mode = t_mod;
    config_table[t_num].t_val  = t_val;
  }
  else
  {
    TRACE_ERROR ("tim_config_timer(): index out of range");
  }
}
#endif /* #if defined (OPTION_TIMER) */

#if defined (NEW_FRAME)
/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)         MODULE  : MM_TIM                   |
| STATE   : code                  ROUTINE : tim_exec_timeout         |
+--------------------------------------------------------------------+

  PURPOSE : execute timeout

*/

GLOBAL void tim_exec_timeout (USHORT index)
{
  GET_INSTANCE_DATA;
  /* typedef void (*T_VOID_FUNC)(); */ /* Already defined */

  static const T_VOID_FUNC timer_jump_table[NUM_OF_MM_TIMERS] = 
  {
    tim_t_reg,    /* T_REGISTRATION */
    tim_t3210,    /* T3210 */
    tim_t3211,    /* T3211 */
    tim_t3212,    /* T3212 */
    tim_t3213,    /* T3213 */
    tim_t3220,    /* T3220 */
    tim_t3230,    /* T3230 */
    tim_t3240,    /* T3240 */

    tim_t_hplmn   /* T_HPLMN */
#ifdef REL99
    , tim_t3241    /* T3241 */
#endif
  };

  if (index < NUM_OF_MM_TIMERS)
  {
    /* 
     * Timeout is handled in SDL like the reception of a primitive, 
     * so enable also this trace if primitive traces are enabled only
     */
/* Implements Measure#36 */
#if defined(NCONFIG)
    /* partab is not defined when NCONFIG is defined */
    TRACE_EVENT_P1 ("tim_exec_timeout: index (%d)", index);
#else /* not (NCONFIG) */
#if defined (TRACE_PRIM) AND defined(OPTION_TIMER)
    TRACE_EVENT_P1 ("tim_exec_timeout (%s)", partab[index].keyword);
#endif
#endif /* NCONFIG */

    mm_data->t_running[index] = FALSE;
    timer_jump_table[index]();  
  }
  else
  {
    TRACE_ERROR ("tim_exec_timeout(): index out of range");
  }
}


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

  PURPOSE : stop timer

*/

GLOBAL void tim_stop_timer (USHORT index)
{
  GET_INSTANCE_DATA;
  assert (index < NUM_OF_MM_TIMERS);

  if (index < NUM_OF_MM_TIMERS)
  {
/* Implements Measure#36 */
#if defined(NCONFIG)
    /* partab is not defined when NCONFIG is defined */
  TRACE_EVENT_P1 ("tim_stop_timer: index (%d)", index);
#else /* not (NCONFIG) */
#if defined (TRACE_PRIM) AND defined(OPTION_TIMER)
  TRACE_EVENT_P1 ("tim_stop_timer (%s)", partab[index].keyword);
#endif
#endif /* NCONFIG */
  
  mm_data->t_running[index] = FALSE;
  TIMER_STOP (mm_handle, index);
}
  else
  {
    TRACE_ERROR ("tim_stop_timer(): index out of range");
  }
}

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

  PURPOSE : start timer

*/

GLOBAL void tim_start_timer (USHORT index, T_TIME value)
{
  GET_INSTANCE_DATA;
  assert (index < NUM_OF_MM_TIMERS);

  if (index < NUM_OF_MM_TIMERS)
  {
/* Implements Measure#36 */
#if defined(NCONFIG)
    /* partab is not defined when NCONFIG is defined */
  TRACE_EVENT_P1 ("tim_start_timer: index (%d)", index);
#else /* not (NCONFIG) */
#if defined (TRACE_PRIM) AND defined(OPTION_TIMER)
  TRACE_EVENT_P1 ("tim_start_timer (%s)", partab[index].keyword);
#endif
#endif /* NCONFIG */
#if defined (OPTION_TIMER)
  switch (config_table[index].t_mode)
  {
    case TIMER_SET:
      value = config_table[index].t_val;
      break;

    case TIMER_RESET:
      value = value;
      break;

    case TIMER_SPEED_UP:
      value = value / config_table[index].t_val;
      if (value == 0)
        value = 1;
      TRACE_EVENT_P1 ("timer_speed_up (%d)", value);
      break;

    case TIMER_SLOW_DOWN:
      value = value * config_table[index].t_val;
      TRACE_EVENT_P1 ("timer_speed_down (%d)", value);
      break;

    default:
      TRACE_FUNCTION ("ERROR: UNKNOWN MODE");
      return;
  }
#endif

  mm_data->t_running[index] = TRUE;

  TIMER_START (mm_handle, index, value);
}
  else
  {
    TRACE_ERROR ("tim_start_timer(): index out of range");
  }
}

#endif /* #if defined (NEW_FRAME) */

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

  PURPOSE : Timeout of timer T3210

*/

GLOBAL void tim_t3210 (void)
{
  GET_INSTANCE_DATA;
  TRACE_FUNCTION ("tim_t3210()");

  switch (GET_STATE (STATE_MM))
  {
    case MM_LUP_INITIATED:
    case MM_WAIT_FOR_RR_CONN_LUP:
      TIMERSTOP (T3240);
      mm_abort_connection (ABCS_NORM);
      /* 
       * The RR connection is aborted normally. RR guarantees that this will
       * be answered by RR_RELEASE_IND.
       * This has the advange that GMM gets the MMGMM_NREG_IND after the
       * channel release of layer 2 if GPRS present without any disadvantage 
       * for a GSM only protocol stack. No state change here.
       */
      break;

    default: /* Ignore event */
      break;
  }
}

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

  PURPOSE : Timeout of timer T3211

*/

// T3211 handling routine does the same as T3213 handling routine now ...

GLOBAL void tim_t3211 (void)
{
  GET_INSTANCE_DATA;
  TRACE_FUNCTION ("tim_t3211()");

  switch (GET_STATE (STATE_MM))
  {
// If we leave these states and reenter a full service IDLE state,
// mm_release_rr_connection() 
// for MM_WAIT_FOR_OUTG_MM_CONN and MM_WAIT_FOR_RR_CONN_MM will 
// handle the update.
// mm_rr_activate_cnf() for MM_WAIT_FOR_RR_ACTIVE will also 
// handle the an outstanding update if coming back to full 
// service IDLE state.
// mm_rr_abort_ind() will be called if the state was 
// MM_PLMN_SEARCH_NORMAL_SERVICE, if there is an outstanding
// updating procedure and the new service is full service, 
// this will be checked there after state transition.
// MM_IDLE_NO_CELL_AVAILABLE / RR_ACTIVATE_IND is handled also.
// No need to store the timer anymore into the queue.

/*
    // case MM_WAIT_FOR_OUTG_MM_CONN:
    // case MM_WAIT_FOR_RR_CONN_MM:
    // case MM_WAIT_FOR_RR_ACTIVE:
    case MM_PLMN_SEARCH_NORMAL_SERVICE:
    // case MM_IDLE_NO_CELL_AVAILABLE:
      mm_write_entry (TIMEOUT, T3211, 0);
      break;
*/

    case MM_IDLE_ATTEMPT_TO_UPDATE:
    case MM_IDLE_NORMAL_SERVICE:
      mm_continue_running_update ();
      break;

    default: /* Ignore event */
      break;
  }
}

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

  PURPOSE : Timeout of counter timer for timer T3212

*/

GLOBAL void tim_t3212 (void)
{
  GET_INSTANCE_DATA;
  TRACE_FUNCTION ("tim_t3212()");

  switch (GET_STATE (STATE_MM))
  {
    case MM_NULL:
    case MM_IDLE_NO_IMSI:
      break; /* Forget the event */

    case MM_IDLE_NORMAL_SERVICE:
      mm_data->t3212_timeout = TRUE;
      mm_data->attempt_cnt = 0; /* Expiry of timer T3212 */
      if (!mm_normal_upd_needed())
      {
        /* MM is updated on the cell, no Imm Ass Rej, no cell barred */
        if (mm_lup_allowed_by_gmm())
        {
          mm_periodic_loc_upd ();
        }
        else
        {
          mm_mmgmm_lup_needed_ind (MMGMM_T3212);
          /* No state change, remains in MM_IDLE_NORMAL_SERVICE */
        }
      }
      break;

    case MM_IDLE_ATTEMPT_TO_UPDATE:
      if (mm_data->mm.mm_info.t3212 NEQ T3212_NO_PRD_UPDAT)
      {
        mm_data->t3212_timeout = TRUE;
        mm_data->attempt_cnt = 0; /* Expiry of timer T3212 */
        if (mm_lup_allowed_by_gmm())
        {
          mm_normal_loc_upd ();
        }
        else
        {
          mm_mmgmm_lup_needed_ind (MMGMM_T3212);
          /* No state change, remains in MM_IDLE_ATTEMPT_TO_UPDATE */
        }
      }
      break;

    default: /* Store the event until it is possible to handle it */
      mm_data->t3212_timeout = TRUE;
      break;
  }
}

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

  PURPOSE : Timeout of timer T3213

*/

GLOBAL void tim_t3213 (void)
{
  GET_INSTANCE_DATA;
  TRACE_FUNCTION ("tim_t3213()");

  switch (GET_STATE (STATE_MM))
  {
/* 
    case MM_WAIT_FOR_OUTG_MM_CONN:
    case MM_WAIT_FOR_RR_CONN_MM:
    case MM_WAIT_FOR_RR_ACTIVE:
    case MM_PLMN_SEARCH_NORMAL_SERVICE:
      mm_write_entry (TIMEOUT, T3213, 0);
      break;
*/

    case MM_IDLE_ATTEMPT_TO_UPDATE:
    case MM_IDLE_NORMAL_SERVICE:
      /* 
       * if something is received from RR or T3213 was already restarted 2 times --> delay of additional 8 seconds
       * continue the LUP attempts
       */
      mm_data->t3213_restart++;
      if (mm_data->t3213_restart > MAX_REST_T3213)
        mm_continue_running_update ();
      else
        TIMERSTART (T3213, T_3213_VALUE);
      break;

    default: /* Ignore event */
      break;
  }
}

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

  PURPOSE : Timeout of timer T3220

*/

GLOBAL void tim_t3220 (void)
{
  GET_INSTANCE_DATA;
  TRACE_FUNCTION ("tim_t3220()");

  switch (GET_STATE (STATE_MM))
  {
    case MM_IMSI_DETACH_INIT:
    case MM_WAIT_FOR_RR_CONN_DETACH:
      /* 
       * The RR connection is aborted normally. RR guarantees that this will
       * be answered by RR_RELEASE_IND. If MM receives the RR_RELEASE_IND,
       * the IMSI DETACH procedure ends and appropriate actions are taken.
       * This has the advange that GMM gets the MMGMM_NREG_CNF after the 
       * channel release of layer 2 if GPRS present without any disadvantage 
       * for a GSM only protocol stack. No state change here.
       */
      mm_abort_connection (ABCS_NORM);
      break;

    default: /* Ignore event */
      break;
  }
}

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

  PURPOSE : Timeout of timer T3230

*/

GLOBAL void tim_t3230 (void)
{
  GET_INSTANCE_DATA;
  TRACE_FUNCTION ("tim_t3230()");

  /* 
   * If T3230 expires (i.e. no response is given but a RR connection is 
   * available) the MM connection establishment is aborted and the requesting
   * CM sublayer is informed. If no other MM connection exists then the mobile
   * station shall proceed as described in section 4.5.3.1 for release of the 
   * RR connection. Otherwise the mobile station shall return to the MM 
   * sublayer state where the request of an MM connection was received,
   * i.e. to MM sublayer state MM connection active. Other ongoing
   * MM connections (if any) shall not be affected.
   * [GSM 04.08 subclause 4.5.1.2 b)]
   * 
   * If all MM connections are released by their CM entities, the
   * mobile station shall set timer T3240 and enter the state
   * WAIT FOR NETWORK COMMAND, expecting the release of the RR connection.
   *  [Excerpt from GSM 04.08 subclause 4.5.3.1]
   */
  
  switch (GET_STATE (STATE_MM))
  {
    case MM_WAIT_FOR_REESTABLISH:
    case MM_WAIT_FOR_OUTG_MM_CONN:
      mm_mmxx_rel_ind (MMCS_TIMER_RECOVERY, CM_ACTIVE);
      mm_mmxx_rel_ind (MMCS_TIMER_RECOVERY, CM_PENDING);
      mm_data->wait_for_accept = FALSE;
      TIMERSTART (T3240, T_3240_VALUE);
      SET_STATE (STATE_MM, MM_WAIT_FOR_NW_CMD);
      break;

    case MM_CONN_ACTIVE: /* wait_for_accept expected to be TRUE */
      mm_mmxx_rel_ind (MMCS_TIMER_RECOVERY, CM_PENDING);
      mm_data->wait_for_accept = FALSE;
      if ((mm_count_connections (CM_ACTIVE) EQ 0) AND
          (mm_count_connections (CM_STORE)  EQ 0))
      {
        TIMERSTART (T3240, T_3240_VALUE);
        SET_STATE (STATE_MM, MM_WAIT_FOR_NW_CMD);
      }
      USE_STORED_ENTRIES();
      break;

    default: /* Ignore event */
      break;
  }
}

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

  PURPOSE : Timeout of timer T3240

*/

GLOBAL void tim_t3240 (void)
{
  GET_INSTANCE_DATA;
  TRACE_FUNCTION ("tim_t3240()");

  switch (GET_STATE (STATE_MM))
  {
    case MM_PROCESS_PROMPT:
    case MM_WAIT_FOR_NW_CMD:
      mm_abort_connection (ABCS_NORM);
      mm_mmxx_rel_ind (MMCS_TIMER_RECOVERY, CM_ACTIVE);
      mm_mmxx_rel_ind (MMCS_TIMER_RECOVERY, CM_PENDING);
      
      /* 
       * The RR connection is aborted normally. RR guarantees that this will
       * be answered by RR_RELEASE_IND.
       * This has the advange that GMM gets the MMGMM_CM_RELEASE_IND after the
       * channel release of layer 2 if GPRS present without any disadvantage 
       * for a GSM only protocol stack. No state change here.
       */
      break;

    case MM_LUP_REJECTED:
      mm_abort_connection (ABCS_NORM);
      /* 
       * The RR connection is aborted normally. RR guarantees that this will
       * be answered by RR_RELEASE_IND.
       * This has the advange that GMM gets the MMGMM_NREG_IND after the
       * channel release of layer 2 if GPRS present without any disadvantage 
       * for a GSM only protocol stack. No state change here.
       */
      break;

    default: /* Ignore event */
      break;
  }
}

#ifdef REL99
/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)       MODULE  : MM_TIM                     |
| STATE   : code                ROUTINE : tim_t3241                  |
+--------------------------------------------------------------------+

  PURPOSE : Timeout of timer T3241

*/

GLOBAL void tim_t3241 (void)
{
  GET_INSTANCE_DATA;
  TRACE_FUNCTION ("tim_t3241()");

  switch (GET_STATE (STATE_MM))
  {
    case MM_RR_CONN_RELEASE_NOT_ALLOWED:
      mm_abort_connection (ABCS_NORM);
      /*
       * The RR connection is aborted normally. RR guarantees that this will
       * be answered by RR_RELEASE_IND.
       * This has the advange that GMM gets the MMGMM_CM_RELEASE_IND after the
       * channel release of layer 2 if GPRS present without any disadvantage
       * for a GSM only protocol stack. No state change here.
       */
      break;
    default: /* Ignore event */
      break;
  }
}
#endif


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

  PURPOSE : Timeout of timer T_HPLMN. 
            This timer allows control of the PPLMN rescan and
            national roaming procedure to recover the HPLMN.
*/

GLOBAL void tim_t_hplmn (void)
{
  GET_INSTANCE_DATA;
  TRACE_FUNCTION ("tim_t_hplmn()");

  /*
   * Launch the scan procedure if it still makes sense to do it according to
   * MM state.
   */
  if (mm_full_service_pplmn_scan())
  {
    mm_data->plmn_scan_mm = TRUE;
    mm_data->first_attach_mem = FALSE;	
    mm_func_mmgmm_net_req();
  }
}


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

  PURPOSE : Timeout of timer T_REG. 
            This timer is not foreseen by the recommendations. 
            It is MM's health monitor timer, checking 
            the conditions whether an update has been missed.

*/

GLOBAL void tim_t_reg (void)
{
  GET_INSTANCE_DATA;
  TRACE_FUNCTION ("tim_t_reg()");

  switch (GET_STATE (STATE_MM))
  {
    case MM_NULL:
      break; /* No timer activity in this state */
  
    case MM_IDLE_NORMAL_SERVICE: /* 19.1 */
      /* Restart the registration timer */
      TIMERSTART (T_REGISTRATION, T_REG_VALUE);      
    
      /* 
       * As it is not expected that the timer catches in state 
       * MM_IDLE_NORMAL_SERVICE in FTA, there is no test 
       * here for a test SIM. The goal is to have an MM
       * where the timer never catches in MM_IDLE_NORMAL_SERVICE.
       */
      
      /* Check whether T3211, T3213 are running retriggering update anyway */
      if ((TIMERACTIVE (T3211) OR TIMERACTIVE (T3213)) AND 
          (mm_data->loc_upd_type.lut NEQ NOT_RUNNING))
        return;

      /* Check whether MM is temporary barred and cannot update now */
      if ((mm_data->idle_entry EQ RRCS_ACCESS_BARRED) OR
          (mm_data->idle_entry EQ RRCS_RND_ACC_DELAY))
        return; 
        
      /* 
       * Check whether we are in an ATTACH update procedure, but there is no
       * T3211, T3213 timer running and MM is not temporary barred.
       */
      if (mm_attach_upd_needed() OR mm_normal_upd_needed())
      {
        TRACE_ERROR ("Recover ATTACH/NORMAL");
        
        mm_mmgmm_lup_needed_ind (MMGMM_REG_TIMER);

        if (mm_normal_upd_needed ())
        {
          mm_data->attempt_cnt = 0; /* New location area */
          mm_normal_loc_upd ();
        }
        else
        {
          mm_attach_loc_upd ();
        }
        return;
      }
 
      /* 
       * Check whether T3212 should run, but is not running.
       * If so, something irregular has happened and 
       * we have to start the update immediately.
       */
      if (mm_data->mm.mm_info.t3212 NEQ T3212_NO_PRD_UPDAT AND 
          (!TIMERACTIVE (T3212) OR mm_data->t3212_timeout))
      {
        /* 
         * The networks says we have periodic updating, 
         * but unexpectedly T3212 is not running or T3212 timed out.
         */
        TRACE_ERROR ("Recover PERIODIC");
        
        if (mm_lup_allowed_by_gmm())
        {
          mm_periodic_loc_upd ();
        }
        else
        {
          // Don't add recovery code now for GPRS, maybe more has to be done.
          // mm_mmgmm_lup_needed_ind (MMGMM_REG_TIMER);
        }
        return;
      }
      break;

    case MM_IDLE_ATTEMPT_TO_UPDATE: /* 19.2 */
      /* Restart the registration timer */
      TIMERSTART (T_REGISTRATION, T_REG_VALUE);      

      /* Timer only handled in this state if a normal SIM is present */
      if (mm_data->reg.op.sim_ins EQ SIM_NO_INSRT OR 
          mm_data->reg.op.ts EQ TS_AVAIL)
        return;

      /* Check whether T3211, T3213 are running retriggering update anyway */
      if ((TIMERACTIVE (T3211) OR TIMERACTIVE (T3213)) AND 
          (mm_data->loc_upd_type.lut NEQ NOT_RUNNING))
        return;

      if (TIMERACTIVE (T3212))
        return;


      /* Check whether MM is temporary barred and cannot update now */
      if ((mm_data->idle_entry EQ RRCS_ACCESS_BARRED) OR
          (mm_data->idle_entry EQ RRCS_RND_ACC_DELAY))
        return; 
        
      if (mm_gsm_alone ())
      {
        mm_normal_loc_upd ();
      }
      else
      {
        mm_mmgmm_lup_needed_ind (MMGMM_REG_TIMER);
        /* No state change, remains in MM_IDLE_ATTEMPT_TO_UPDATE */
      }
      break;

    default:
      /* Restart the registration timer */
      TIMERSTART (T_REGISTRATION, T_REG_VALUE);      
      break;
  }
}

#endif