diff src/g23m-gprs/sm/sm_sequencer.c @ 1:fa8dc04885d8

src/g23m-*: import from Magnetite
author Mychaela Falconia <falcon@freecalypso.org>
date Fri, 16 Oct 2020 06:25:50 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/g23m-gprs/sm/sm_sequencer.c	Fri Oct 16 06:25:50 2020 +0000
@@ -0,0 +1,793 @@
+/*----------------------------------------------------------------------------
+|  Project :  3G PS
+|  Module  :  SM
++-----------------------------------------------------------------------------
+|             Copyright 2003 Texas Instruments.
+|             All rights reserved. 
+| 
+|             This file is confidential and a trade secret of Texas 
+|             Instruments.
+|             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. 
++-----------------------------------------------------------------------------
+| Purpose:    Sequencer state machine implementation in the SM entity.
+|             For design details, see:
+|             8010.908 SM Detailed Specification
++---------------------------------------------------------------------------*/
+
+/*==== DECLARATION CONTROL =================================================*/
+
+/*==== INCLUDES =============================================================*/
+
+#include "sm.h"
+
+#include "sm_sequencer.h"
+#include "sm_context_control.h"
+
+#include "sm_aci_output_handler.h"
+#include "sm_mm_output_handler.h"
+#include "sm_qos.h"
+
+/*==== CONSTS ===============================================================*/
+
+/*==== TYPES ================================================================*/
+
+typedef void (*T_SM_SEQUENCER_TRANSITION_FUNC)(void *data);
+
+typedef struct {
+#ifdef DEBUG
+  T_SM_SEQUENCER_EVENT         event;
+#endif /* DEBUG */
+  T_SM_SEQUENCER_TRANSITION_FUNC  func;
+} T_SM_SEQUENCER_TRANSITION;
+
+/*==== LOCALS ===============================================================*/
+
+static void handle_mmpm_attach_ind(void *data);
+static void handle_mmpm_detach_ind(void *data);
+static void handle_smreg_pdp_deactivate_req(void *data);
+static void handle_sm_status_req(void *data);
+static void handle_context_activation_override(void *data);
+static void handle_context_deactivate_completed(void *data);
+static void handle_context_tear_down_deactivate(void *data);
+static void handle_context_reactivate_completed(void *data);
+
+/*==== PRIVATE FUNCTIONS ====================================================*/
+
+static const
+T_SM_SEQUENCER_TRANSITION transition[SM_SEQUENCER_NUMBER_OF_EVENTS] =
+{
+  M_TRANSITION(SM_P_MMPM_ATTACH_IND,              handle_mmpm_attach_ind),
+  M_TRANSITION(SM_P_MMPM_DETACH_IND,              handle_mmpm_detach_ind),
+  M_TRANSITION(SM_P_SMREG_PDP_DEACTIVATE_REQ,     handle_smreg_pdp_deactivate_req),
+  M_TRANSITION(SM_P_SM_STATUS_REQ,                handle_sm_status_req),
+  M_TRANSITION(SM_I_CONTEXT_ACTIVATION_OVERRIDE,  handle_context_activation_override),
+  M_TRANSITION(SM_I_CONTEXT_DEACTIVATE_COMPLETED, handle_context_deactivate_completed),
+  M_TRANSITION(SM_I_CONTEXT_TEAR_DOWN_DEACTIVATE, handle_context_tear_down_deactivate),
+  M_TRANSITION(SM_I_CONTEXT_REACTIVATE_COMPLETED, handle_context_reactivate_completed)
+};
+
+static void sm_seq_clear_nsapis_to_deactivate(void)
+{
+  sm_data.sm_nsapis_requested_deactivated = 0;
+  sm_data.sm_nsapis_being_deactivated     = 0;
+}
+
+static void sm_seq_set_nsapis_to_deactivate(U16 nsapi_set)
+{
+  sm_data.sm_nsapis_requested_deactivated |= nsapi_set;
+}
+
+static U16 sm_seq_get_nsapis_to_deactivate(void)
+{
+  return (sm_data.sm_nsapis_requested_deactivated);
+}
+
+static BOOL sm_seq_is_nsapi_requested_deactivated(int /*@alt U8@*/ nsapi)
+{
+  return sm_is_nsapi_in_nsapi_set(nsapi, sm_data.sm_nsapis_requested_deactivated);
+}
+
+static void sm_seq_nsapi_started_deactivation(int /*@alt U8@*/ nsapi)
+{
+  sm_data.sm_nsapis_being_deactivated
+    = sm_add_nsapi_to_nsapi_set(nsapi, sm_data.sm_nsapis_being_deactivated);
+}
+
+static void sm_seq_nsapi_completed_deactivation(int /*@alt U8@*/ nsapi)
+{
+  sm_data.sm_nsapis_being_deactivated
+    = sm_remove_nsapi_from_nsapi_set(nsapi, sm_data.sm_nsapis_being_deactivated);
+}
+
+static BOOL sm_seq_is_nsapi_set_completed(void)
+{
+  return (sm_data.sm_nsapis_being_deactivated == 0);
+}
+
+/*
++------------------------------------------------------------------------------
+| Function    : handle_mmpm_attach_ind
++------------------------------------------------------------------------------
+| Description : Handle incoming MMPM_ATTACH_IND
+|
+| Parameters  : data              - T_MMPM_ATTACH_IND *
++------------------------------------------------------------------------------
+*/ 
+
+#ifndef TI_PS_OP_SM_RETAIN_BEST_CONTEXT
+/*Old method. Secondary contexts deactivated after a R99 to R97 RAU*/
+static void handle_mmpm_attach_ind(void *data)
+{
+  int                nsapi;
+  T_PS_sgsn_rel      sgsn_rel;
+  T_MMPM_ATTACH_IND *prim = (T_MMPM_ATTACH_IND *)data;
+#ifdef DEBUG
+  /*@observer@*/
+  static const char *sgsn_rel_name[3] = {
+    "PS_SGSN_UNKNOWN",
+    "PS_SGSN_98_OLDER",
+    "PS_SGSN_99_ONWARDS"
+  };
+#endif
+
+  (void)TRACE_FUNCTION("handle_mmpm_attach_ind");
+
+  sgsn_rel = (T_PS_sgsn_rel)cl_nwrl_get_sgsn_release();
+
+  (void)TRACE_EVENT_P1("SGSN release given by CL is (1->R97, 2->R99) >>> %d", 
+                        cl_nwrl_get_sgsn_release());
+
+#ifdef DEBUG
+  (void)TRACE_EVENT_P2("Got SGSN release value 0x%02x: %s",
+                       sgsn_rel,
+                       (sgsn_rel <= PS_SGSN_99_ONWARDS
+                        ? sgsn_rel_name[(U16)sgsn_rel]
+                        : "BAD VALUE!"));
+#endif
+
+  /* Check whether the UE has moved from R99 to R97/R98 network. */
+  if (sm_get_current_nw_release() == PS_SGSN_99_ONWARDS &&
+      sgsn_rel == PS_SGSN_98_OLDER) {
+    /* We have moved from R99 network to pre-R99:
+     * check for secondary contexts, or contexts with TI > 6;
+     * These must be locally deactivated. */
+    U16              nsapis_to_deactivate = 0;
+
+    for (nsapi = (int)NAS_NSAPI_5; nsapi < NAS_SIZE_NSAPI; nsapi++)
+    {
+      struct T_SM_CONTEXT_DATA *context;
+
+      context = sm_get_context_data_from_nsapi(nsapi);
+      if (context != NULL &&
+	  (context->ti > (U8)SM_MAX_NON_EXT_TI || sm_is_secondary(context)))
+      {
+	    nsapis_to_deactivate = sm_add_nsapi_to_nsapi_set(nsapi, nsapis_to_deactivate);
+            /* Make sure to clear the pending reactivation flag */
+            sm_set_context_pending_reactivation(context, FALSE);
+	    /* Order context deactivation */
+	    sm_context_control(context, SM_I_CONTEXT_LOCAL_DEACTIVATE, (void *)TRUE);
+	  }
+    }
+
+    /* If any contexts were deactivated, inform ACI and MM */
+    if (nsapis_to_deactivate != 0)
+    {
+      T_CAUSE_ps_cause cause;
+      cause.ctrl_value      = CAUSE_is_from_sm;
+      cause.value.sm_cause  = (U16)CAUSE_SM_PRE_R99_NETWORK_ENTERED;
+
+      send_smreg_pdp_deactivate_ind(nsapis_to_deactivate, &cause);
+      send_mmpm_pdp_context_status_req();
+    }
+  }
+
+  /* Check whether any (primary) contexts are pending reactivation:
+   * If so, start reactivation now. */
+  for (nsapi = (int)NAS_NSAPI_5; nsapi < NAS_SIZE_NSAPI; nsapi++)
+  {
+    struct T_SM_CONTEXT_DATA *context;
+
+    context = sm_get_context_data_from_nsapi(nsapi);
+    if (context != NULL && !sm_is_secondary(context)
+        && sm_is_context_pending_reactivation(context))
+    {
+      /* Clear the pending reactivation flag. Will be done in 
+         sm_is_address_changed_with_reactivation */
+      /*sm_set_context_pending_reactivation(context, FALSE);*/
+
+      /* Order context reactivation */
+      sm_context_control(context, SM_I_CONTEXT_REACTIVATE, NULL);
+    }
+  }
+
+  /* Set current RAT and core network release */
+  sm_set_current_rat       ((T_PS_rat)prim->rat);
+  sm_set_current_nw_release(sgsn_rel);
+
+  /*
+   * The following line is useful if we try a PDP activation after a 
+   * GPRS detach
+   */
+  sm_data.sm_suspended = FALSE;
+  sm_data.sm_suspend_cause = CAUSE_MM_SUCCESS;
+}
+
+#else /*#ifndef TI_PS_OP_SM_RETAIN_BEST_CONTEXT*/
+/*New method. The best context remain after a R99 to R97 RAU*/
+
+static void handle_mmpm_attach_ind(void *data)
+{
+  int                nsapi;
+  T_PS_sgsn_rel      sgsn_rel;
+  T_MMPM_ATTACH_IND  *prim = (T_MMPM_ATTACH_IND *)data;
+  U16                nsapis_to_deactivate = 0;
+  U16                nsapis_deactivated   = 0;
+
+#ifdef DEBUG
+  /*@observer@*/
+  static const char *sgsn_rel_name[3] = {
+    "PS_SGSN_UNKNOWN",
+    "PS_SGSN_98_OLDER",
+    "PS_SGSN_99_ONWARDS"
+  };
+#endif
+
+  (void)TRACE_FUNCTION("handle_mmpm_attach_ind");
+
+  sgsn_rel = cl_nwrl_get_sgsn_release();
+
+  (void)TRACE_EVENT_P1("SGSN release given by CL is (1->R97, 2->R99) >>> %d", 
+                        cl_nwrl_get_sgsn_release());
+
+#ifdef DEBUG
+  (void)TRACE_EVENT_P2("Got SGSN release value 0x%02x: %s",
+                       sgsn_rel,
+                       (sgsn_rel <= PS_SGSN_99_ONWARDS
+                        ? sgsn_rel_name[(U16)sgsn_rel]
+                        : "BAD VALUE!"));
+#endif
+
+  /* Check whether the UE has moved from R99 to R97/R98 network. */
+  if (sm_get_current_nw_release() == PS_SGSN_99_ONWARDS &&
+      sgsn_rel == PS_SGSN_98_OLDER) {
+    /* We have moved from R99 network to pre-R99:
+     * First round of elimination. Check for contexts with TI > 6;
+     * These must be locally deactivated. 
+     */
+
+    for (nsapi = (int)NAS_NSAPI_5; nsapi < NAS_SIZE_NSAPI; nsapi++)
+    {
+      struct T_SM_CONTEXT_DATA *context;
+
+      context = sm_get_context_data_from_nsapi(nsapi);
+      if (context != NULL && (context->ti > (U8)SM_MAX_NON_EXT_TI ))
+      {
+        nsapis_to_deactivate = sm_add_nsapi_to_nsapi_set(nsapi, 
+                                         nsapis_to_deactivate);
+        /* Make sure to clear the pending reactivation flag */
+        sm_set_context_pending_reactivation(context, FALSE);
+        /* Order context deactivation */
+        sm_context_control(context, SM_I_CONTEXT_LOCAL_DEACTIVATE, (void *)TRUE);
+      }		
+    }
+
+   /* Second round of elimination. Rank contexts based on Traffic class and 
+    * traffic handling priority. Locally deactivate all but the best ranking 
+    * context(s). Ref 23.107 Annex C
+    */
+    nsapis_deactivated   =  sm_rank_del_contexts_based_on_tc(); 
+    nsapis_to_deactivate = (nsapis_to_deactivate | nsapis_deactivated);
+
+   /* Third round of elimination. Context(s) with highest values of max bitrate
+    * for uplink or downlink remain. Ref 23.107 Annex C
+    */
+    nsapis_deactivated   =  sm_retain_cntxt_wth_best_bitrate();
+    nsapis_to_deactivate = (nsapis_to_deactivate | nsapis_deactivated);
+
+   /* Final round of elimination. Context with smallest nsapi remain. 
+    * Ref 23.107 Annex C
+    */
+    nsapis_deactivated   =  sm_retain_cntxt_with_least_nsapi();
+    nsapis_to_deactivate = (nsapis_to_deactivate | nsapis_deactivated);
+	
+    /* If any contexts were deactivated, inform ACI and MM */
+    if (nsapis_to_deactivate != 0)
+    {
+      T_CAUSE_ps_cause cause;
+      cause.ctrl_value      = CAUSE_is_from_sm;
+      cause.value.sm_cause  = (U16)CAUSE_SM_PRE_R99_NETWORK_ENTERED;
+
+      send_smreg_pdp_deactivate_ind(nsapis_to_deactivate, &cause);
+      send_mmpm_pdp_context_status_req();
+    }
+
+    /* Now all the remaining contexts should be primary. Clear the 
+     * secondary context flag if it is set in any of the remaining
+     * active context(s). 
+     */
+    for (nsapi = (int)NAS_NSAPI_5; nsapi < NAS_SIZE_NSAPI; nsapi++)
+    {
+      struct T_SM_CONTEXT_DATA *context;
+
+      context = sm_get_context_data_from_nsapi(nsapi);
+      if ( (context != NULL) && (context->context_control_state
+                    != SM_CONTEXT_DEACTIVATED)  && sm_is_secondary(context))
+      { /* Clear the secondary context flag */
+        context->flags &= 0xff^(U8)SM_CONTEXT_FLAG_SECONDARY_CONTEXT;
+      }
+    }
+  }
+
+  /* Check whether any (primary) contexts are pending reactivation:
+   * If so, start reactivation now. 
+   */
+  for (nsapi = (int)NAS_NSAPI_5; nsapi < NAS_SIZE_NSAPI; nsapi++)
+  {
+    struct T_SM_CONTEXT_DATA *context;
+
+    context = sm_get_context_data_from_nsapi(nsapi);
+    if (context != NULL && !sm_is_secondary(context)
+        && sm_is_context_pending_reactivation(context))
+    {
+      /* Clear the pending reactivation flag. Will be done in 
+         sm_is_address_changed_with_reactivation */
+      //sm_set_context_pending_reactivation(context, FALSE);
+
+      /* Order context reactivation */
+      sm_context_control(context, SM_I_CONTEXT_REACTIVATE, NULL);
+    }
+  }
+
+  /* Set current RAT and core network release */
+  sm_set_current_rat       ((T_PS_rat)prim->rat);
+  sm_set_current_nw_release(sgsn_rel);
+
+  /*
+   * The following line is useful if we try a PDP activation after a 
+   * GPRS detach
+   */
+  sm_data.sm_suspended = FALSE;
+  sm_data.sm_suspend_cause = CAUSE_MM_SUCCESS;
+}
+#endif /*#ifndef TI_PS_OP_SM_RETAIN_BEST_CONTEXT*/
+
+/*
++------------------------------------------------------------------------------
+| Function    : handle_mmpm_detach_ind
++------------------------------------------------------------------------------
+| Description : Handle incoming MMPM_DETACH_IND primitive
+|
+| Parameters  : data              - T_MMPM_DETACH_IND *
++------------------------------------------------------------------------------
+*/
+static void handle_mmpm_detach_ind(void *data)
+{
+  T_MMPM_DETACH_IND *prim = data;
+  int   nsapi;
+  U16   nsapis_to_deactivate = 0;
+  BOOL  cause_reattach;
+
+  (void)TRACE_FUNCTION("handle_mmpm_detach_ind");
+
+  TRACE_ASSERT (data != NULL);
+
+  if (prim->ps_cause.ctrl_value     == CAUSE_is_from_mm &&
+      prim->ps_cause.value.mm_cause == (U16)CAUSE_MM_DETACH_WITH_REATTACH)
+  {
+    cause_reattach = TRUE;
+  } else {
+    cause_reattach = FALSE;
+  }
+
+  for (nsapi = (int)NAS_NSAPI_5; nsapi < NAS_SIZE_NSAPI; nsapi++)
+  {
+    struct T_SM_CONTEXT_DATA *context;
+
+    context = sm_get_context_data_from_nsapi(nsapi);
+    if (context != NULL)
+    {
+      if (cause_reattach)
+      {
+        sm_set_context_pending_reactivation(context, TRUE);
+      } else {
+        sm_set_context_pending_reactivation(context, FALSE);
+        nsapis_to_deactivate = sm_add_nsapi_to_nsapi_set(nsapi, nsapis_to_deactivate);
+      }
+
+      sm_context_control(context, SM_I_CONTEXT_LOCAL_DEACTIVATE, (void *)TRUE);
+    }
+  }
+
+  /* Inform ACI that the contexts are gone. */
+  if (nsapis_to_deactivate != 0)
+  {
+    send_smreg_pdp_deactivate_ind(nsapis_to_deactivate, &prim->ps_cause);
+  }
+}
+
+/*
++------------------------------------------------------------------------------
+| Function    : handle_smreg_pdp_deactivate_req
++------------------------------------------------------------------------------
+| Description : Handle incoming SMREG_PDP_DEACTIVATE_REQ
+|
+| Parameters  : data              - T_SMREG_PDP_DEACTIVATE_REQ *
++------------------------------------------------------------------------------
+*/
+static void handle_smreg_pdp_deactivate_req(void *data)
+{
+  int                         nsapi;
+  U16                         nsapis_deactivated = 0;
+  T_SMREG_PDP_DEACTIVATE_REQ *prim = (T_SMREG_PDP_DEACTIVATE_REQ *)data;
+  (void)TRACE_FUNCTION("handle_smreg_pdp_deactivate_req");
+
+  TRACE_ASSERT(prim != NULL);
+
+  if (prim EQ NULL) 
+    return;
+
+  for (nsapi = (int)NAS_NSAPI_5; nsapi < NAS_SIZE_NSAPI; nsapi++)
+  {
+    if (sm_is_nsapi_in_nsapi_set(nsapi, prim->nsapi_set))
+    {
+      struct T_SM_CONTEXT_DATA *context;
+
+      context = sm_get_context_data_from_nsapi(nsapi);
+      if (context != NULL)
+      {
+	nsapis_deactivated = sm_add_nsapi_to_nsapi_set(nsapi, nsapis_deactivated);
+
+        /* Set SM_CONTEXT_FLAG_STARTED_DURING_SUSPEND flag according to suspend status */
+        sm_set_started_during_suspend(context);
+
+        /* Clear pending reactivation flag */
+        sm_set_context_pending_reactivation(context, FALSE);
+
+        /* Mark requested nsapi for deactivation */
+        sm_seq_set_nsapis_to_deactivate(sm_nsapi2nsapi_set(nsapi));
+
+	/* Mark NSAPI "deactivation in progress" */
+	sm_seq_nsapi_started_deactivation(nsapi);
+
+	if (prim->rel_ind == (U8)PS_REL_IND_YES)
+        {
+	  /* Local deactivation */
+	  sm_context_control(context, SM_I_CONTEXT_LOCAL_DEACTIVATE, (void *)TRUE);
+	} else {
+	  /* Deactivation with network signalling */
+	  sm_context_control(context, SM_I_CONTEXT_DEACTIVATE,
+                             (void *)SM_I_DEACTIVATE_NETWORK_AND_USER_PLANE);
+	} /* if */
+      } /* if */
+    } /* if */
+  } /* for */
+
+  if (nsapis_deactivated != 0) {
+    if (prim->rel_ind == (U8)PS_REL_IND_YES) {
+      /* For local deactivations, inform MM if any contexts were deactivated */
+      send_mmpm_pdp_context_status_req();
+      /* Inform ACI of the deactivated contexts */
+      send_smreg_pdp_deactivate_cnf(prim->nsapi_set);
+    }
+  } else {
+    /* Contexts must already be deactivated - inform ACI */
+    send_smreg_pdp_deactivate_cnf(prim->nsapi_set);
+  }
+}
+
+/*
++------------------------------------------------------------------------------
+| Function    : handle_sm_status_req
++------------------------------------------------------------------------------
+| Description : Handle incoming SM_STATUS_REQ
+|
+| Parameters  : data              - T_SM_STATUS_REQ *
++------------------------------------------------------------------------------
+*/
+static void handle_sm_status_req(void *data)
+{
+  int                         nsapi;
+  T_SM_STATUS_REQ            *prim = (T_SM_STATUS_REQ *)data;
+  T_SM_CONTEXT_CONTROL_EVENT  event;
+  void                       *edata;
+  (void)TRACE_FUNCTION("handle_sm_status_req");
+
+  if (prim->ps_cause.ctrl_value == CAUSE_is_from_llc) {
+    switch (prim->ps_cause.value.llc_cause) {
+    case CAUSE_LLC_NO_PEER_RES:
+      /* Do nothing */
+      return;
+    case CAUSE_LLC_NORMAL_REL:      /* Normal deactivation */
+    default:      /* Unknown user plane error:  Deactivate contexts */
+      /* Deactivate with network signalling */
+      event = SM_I_CONTEXT_USER_PLANE_ERROR;
+      edata = NULL;
+      break;
+    } /* switch */
+  } else if (prim->ps_cause.ctrl_value == CAUSE_is_from_upm) {
+    switch (prim->ps_cause.value.upm_cause) {
+    case CAUSE_UPM_RT_QOS_RELEASE:
+      /* Inform ACI of QoS downgrade */
+      send_smreg_pdp_modify_ind_multiple(prim->nsapi_set,
+                                         SM_UPDATE_QOS_DOWNGRADE);
+      return;
+    case CAUSE_UPM_REEST_NEEDED:
+      event = SM_I_CONTEXT_UPGRADE;
+      edata = NULL;
+      break;
+    case CAUSE_UPM_REEST_REJECT:
+      /* FIXME:  How to handle reactivation reject? */
+      return;
+    case CAUSE_UPM_NORMAL_RELEASE:
+      event = SM_I_CONTEXT_USER_PLANE_ERROR;
+      edata = NULL;
+      break;
+    default: /* Unknown user plane error:  Deactivate contexts */
+      event = SM_I_CONTEXT_DEACTIVATE;
+      edata = (void *)SM_I_DEACTIVATE_NETWORK_AND_USER_PLANE;
+      break;
+    } /* switch */
+  } else {
+    /* Unknown user plane error:  Assume user plane down and deactivate contexts */
+    event = SM_I_CONTEXT_USER_PLANE_ERROR;
+    edata = NULL;
+  }
+
+  for (nsapi = (int)NAS_NSAPI_5; nsapi < NAS_SIZE_NSAPI; nsapi++)
+  {
+    if (sm_is_nsapi_in_nsapi_set(nsapi, prim->nsapi_set))
+    {
+      struct T_SM_CONTEXT_DATA *context;
+
+      context = sm_get_context_data_from_nsapi(nsapi);
+      if (context != NULL) {
+	sm_set_aci_cause(context, prim->ps_cause.ctrl_value,
+			 prim->ps_cause.value.nwsm_cause);
+	sm_context_control(context, event, edata);
+      } /* if */
+    } /* if */
+  } /* for */
+}
+
+/*
++------------------------------------------------------------------------------
+| Function    : handle_context_activation_override
++------------------------------------------------------------------------------
+| Description : Handle SM_I_CONTEXT_ACTIVATION_OVERRIDE
+|
+| Parameters  : data              - ti
++------------------------------------------------------------------------------
+*/
+static void handle_context_activation_override(void *data)
+{
+  int                 nsapi, ti            = (int)data;
+  U16                 nsapis_to_deactivate = 0;
+  (void)TRACE_FUNCTION("handle_context_activation_override");
+
+  for (nsapi = (int)NAS_NSAPI_5; nsapi < NAS_SIZE_NSAPI; nsapi++)
+  {
+    struct T_SM_CONTEXT_DATA *context;
+
+    context = sm_get_context_data_from_nsapi(nsapi);
+    if (context != NULL && context->ti != (U8)ti &&
+	sm_is_secondary(context) && context->linked_ti == (U8)ti)
+    {
+      /* Locally deactivate contexts linked to ti of input context */
+      nsapis_to_deactivate = sm_add_nsapi_to_nsapi_set(nsapi, nsapis_to_deactivate);
+    } /* if */
+  } /* for */
+
+  if (nsapis_to_deactivate != 0)
+  {
+    T_CAUSE_ps_cause cause;
+
+    /* Found contexts to deactivate: inform ACI */
+    cause.ctrl_value     = CAUSE_is_from_sm;
+    cause.value.sm_cause = (U16)CAUSE_SM_ACTIVATION_OVERRIDE;
+
+    send_smreg_pdp_deactivate_ind(nsapis_to_deactivate, &cause);
+
+    /* Perform local deactivation */
+    for (nsapi = (int)NAS_NSAPI_5; nsapi < NAS_SIZE_NSAPI; nsapi++)
+    {
+      if (sm_is_nsapi_in_nsapi_set(nsapi, nsapis_to_deactivate))
+      {
+	struct T_SM_CONTEXT_DATA *context;
+
+	context = sm_get_context_data_from_nsapi(nsapi);
+	sm_context_control(context, SM_I_CONTEXT_LOCAL_DEACTIVATE, (void *)TRUE);
+      } /* if */
+    } /* for */
+  } /* if */
+}
+
+/*
++------------------------------------------------------------------------------
+| Function    : handle_context_deactivate_completed
++------------------------------------------------------------------------------
+| Description : Handle SM_I_CONTEXT_DEACTIVATE_COMPLETED
+|
+| Parameters  : data              - NSAPI
++------------------------------------------------------------------------------
+*/
+static void handle_context_deactivate_completed(void *data)
+{
+  int                 nsapi = (int)data;
+  (void)TRACE_FUNCTION("handle_context_deactivate_completed");
+
+  /* Mark nsapi as deactivated */
+  sm_seq_nsapi_completed_deactivation(nsapi);
+
+  /* Check that this NSAPI is part of the requested NSAPIs being deactivated.
+   * Could also be due to SMREG_CONFIGURE_REQ etc. */
+  if (sm_seq_is_nsapi_requested_deactivated(nsapi)) {
+   /* Yes, NSAPI is in set. Check whether deactivation completes with this NSAPI */
+    if (sm_seq_is_nsapi_set_completed()) {
+      /* Done.  Confirm deactivation towards ACI */
+      send_smreg_pdp_deactivate_cnf(sm_seq_get_nsapis_to_deactivate());
+
+      /* Reset requested deactivation nsapi_set. */
+      sm_seq_clear_nsapis_to_deactivate();
+    }
+  } else {
+    /* No. NSAPI was deactivated for reasons other than deactivation from ACI */
+  }
+}
+
+/*
++------------------------------------------------------------------------------
+| Function    : handle_context_reactivate_completed
++------------------------------------------------------------------------------
+| Description : Handle SM_I_CONTEXT_REACTIVATE_COMPLETED
+|
+| Parameters  : data              - TI (of primary context)
++------------------------------------------------------------------------------
+*/
+static void handle_context_reactivate_completed(void *data)
+{
+  int   ti = (int)data;
+  int   nsapi;
+
+  (void)TRACE_FUNCTION("handle_context_reactivate_completed");
+
+  /* Check whether any secondary contexts are pending reactivation with
+   * linked_ti == ti of the primary context just reactivated.
+   * If so, start reactivation now. */
+  for (nsapi = (int)NAS_NSAPI_5; nsapi < NAS_SIZE_NSAPI; nsapi++)
+  {
+    struct T_SM_CONTEXT_DATA *context;
+
+    context = sm_get_context_data_from_nsapi(nsapi);
+    if (context != NULL && sm_is_secondary(context)
+        && context->linked_ti == (U8)ti)
+    {
+      /* Clear the pending reactivation flag */
+      sm_set_context_pending_reactivation(context, FALSE);
+
+      /* Order context reactivation */
+      sm_context_control(context, SM_I_CONTEXT_REACTIVATE, NULL);
+    }
+  }
+}
+
+/*
++------------------------------------------------------------------------------
+| Function    : handle_context_tear_down_deactivate
++------------------------------------------------------------------------------
+| Description : Handle SM_I_CONTEXT_TEAR_DOWN_DEACTIVATE
+|
+| Parameters  : data              - TI
++------------------------------------------------------------------------------
+*/
+static void handle_context_tear_down_deactivate(void *data)
+{
+  int                 nsapi, ti            = (int)data;
+  U16                 nsapis_to_deactivate = 0;
+  (void)TRACE_FUNCTION("handle_context_tear_down_deactivate");
+
+  nsapis_to_deactivate = sm_linked_nsapis((U8)ti);
+
+  if (nsapis_to_deactivate != 0) {
+    /* Perform local deactivation */
+    for (nsapi = (int)NAS_NSAPI_5; nsapi < NAS_SIZE_NSAPI; nsapi++)
+    {
+      if (sm_is_nsapi_in_nsapi_set(nsapi, nsapis_to_deactivate))
+      {
+	struct T_SM_CONTEXT_DATA *context;
+
+	context = sm_get_context_data_from_nsapi(nsapi);
+	sm_context_control(context, SM_I_CONTEXT_LOCAL_DEACTIVATE, (void *)TRUE);
+      } /* if */
+    } /* for */
+  } /* if */
+}
+
+/*==== PUBLIC FUNCTIONS =====================================================*/
+
+/*
++------------------------------------------------------------------------------
+| Function    : sm_sequencer_init
++------------------------------------------------------------------------------
+| Description : Sequencer state machine initialization function
+|
+| Parameters  : None
++------------------------------------------------------------------------------
+*/
+void sm_sequencer_init(void)
+{
+  (void)TRACE_FUNCTION("sm_sequencer_init");
+
+
+  sm_data.sm_attached = FALSE;
+  sm_data.sm_suspended = FALSE;
+  sm_data.sm_suspend_cause = CAUSE_MM_SUCCESS;
+
+
+  sm_seq_clear_nsapis_to_deactivate();
+}
+
+/*
++------------------------------------------------------------------------------
+| Function    : sm_sequencer_exit
++------------------------------------------------------------------------------
+| Description : Sequencer state machine exit function
+
+| Parameters  : None
++------------------------------------------------------------------------------
+*/
+void sm_sequencer_exit(void)
+{
+  (void)TRACE_FUNCTION("sm_sequencer_exit");
+
+  sm_data.sm_attached = FALSE;
+  sm_seq_clear_nsapis_to_deactivate();
+}
+
+/*
++------------------------------------------------------------------------------
+| Function    : sm_sequencer
++------------------------------------------------------------------------------
+| Description : Sequencer state machine
+|
+| Parameters  : event            - Internal event (see sm_sequencer.h)
+|               data             - Event dependent parameter
++------------------------------------------------------------------------------
+*/
+void sm_sequencer(T_SM_SEQUENCER_EVENT event,
+		  void *data)
+{
+#ifdef DEBUG
+  /*@observer@*/
+  static const char *event_name[SM_SEQUENCER_NUMBER_OF_EVENTS] = {
+    "SM_P_MMPM_ATTACH_IND",
+    "SM_P_MMPM_DETACH_IND",
+    "SM_P_SMREG_PDP_DEACTIVATE_REQ",
+    "SM_P_SM_STATUS_REQ",
+    "SM_I_CONTEXT_ACTIVATION_OVERRIDE",
+    "SM_I_CONTEXT_DEACTIVATE_COMPLETED",
+    "SM_I_CONTEXT_TEAR_DOWN_DEACTIVATE",
+    "SM_I_CONTEXT_REACTIVATE_COMPLETED"
+  };
+  char  state_array[SM_MAX_NSAPI_OFFSET + 1];
+
+  TRACE_ASSERT(event < SM_SEQUENCER_NUMBER_OF_EVENTS);
+
+  if (transition[(U16)event].event != event) {
+    (void)TRACE_ERROR("Event table error in sm_sequencer!");
+  }
+#endif /* DEBUG */
+
+  transition[(U16)event].func(data);
+
+#ifdef DEBUG
+  (void)TRACE_EVENT_P2("SM SEQUENCER : %s => %s",
+		       event_name[(U16)event],
+		       sm_context_bitfield(state_array, sm_data.sm_context_activation_status));
+#endif /* DEBUG */
+}
+
+/*==== END OF FILE ==========================================================*/