view src/g23m-gprs/sm/sm_sequencer.c @ 667:36df58db1640

AT@LPG and AT@PWL implemented in aci2
author Mychaela Falconia <falcon@freecalypso.org>
date Thu, 28 May 2020 01:47:28 +0000 (2020-05-28)
parents 219afcfc6250
children
line wrap: on
line source
/*----------------------------------------------------------------------------
|  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 ==========================================================*/