view src/g23m-gprs/sm/sm_qos.c @ 688:5806383e2788

doc/Compiling: update for luna1, luna2 and tangomdm
author Mychaela Falconia <falcon@freecalypso.org>
date Mon, 12 Oct 2020 19:17:23 +0000
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:    3G QoS utility functions implementation in the SM entity.
|             For design details, see:
|             8010.908 SM Detailed Specification
+---------------------------------------------------------------------------*/

/*==== DECLARATION CONTROL =================================================*/

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

#include "sm.h"

#include "sm_qos.h"

/*==== CONST ================================================================*/

struct qos_from_to {
  U32        integer;
  struct {
    U16      ratio_mant, ratio_exp;
  }          ratio;
  U8         packed;
};

/*@checked@*/
static const struct qos_from_to sm_qos_ber_table[] = {
  {      20UL, {(U16)5,(U16)2}, (U8)M_SM_QOS_BER_5E_2},
  {     100UL, {(U16)5,(U16)2}, (U8)M_SM_QOS_BER_1E_2},
  {     200UL, {(U16)5,(U16)3}, (U8)M_SM_QOS_BER_5E_3},
  {     250UL, {(U16)4,(U16)3}, (U8)M_SM_QOS_BER_4E_3},
  {    1000UL, {(U16)1,(U16)3}, (U8)M_SM_QOS_BER_1E_3},
  {   10000UL, {(U16)1,(U16)4}, (U8)M_SM_QOS_BER_1E_4},
  {  100000UL, {(U16)1,(U16)5}, (U8)M_SM_QOS_BER_1E_5},
  { 1000000UL, {(U16)1,(U16)6}, (U8)M_SM_QOS_BER_1E_6},
  {16666667UL, {(U16)6,(U16)8}, (U8)M_SM_QOS_BER_6E_8},
  {       0UL, {(U16)0,(U16)0}, (U8)M_SM_QOS_BER_SUB}
};

/*@checked@*/
static const struct qos_from_to sm_qos_sdu_err_ratio_table[] = {
  {      10UL, {(U16)1,(U16)1}, (U8)M_SM_QOS_SDU_ERR_1E_1}, /* NOTE: M_SM_QOS_SDU_ERR_1E_1 == 7 */
  {     100UL, {(U16)1,(U16)2}, (U8)M_SM_QOS_SDU_ERR_1E_2},
  {     143UL, {(U16)7,(U16)3}, (U8)M_SM_QOS_SDU_ERR_7E_3},
  {    1000UL, {(U16)1,(U16)3}, (U8)M_SM_QOS_SDU_ERR_1E_3},
  {   10000UL, {(U16)1,(U16)4}, (U8)M_SM_QOS_SDU_ERR_1E_4},
  {  100000UL, {(U16)1,(U16)5}, (U8)M_SM_QOS_SDU_ERR_1E_5},
  { 1000000UL, {(U16)1,(U16)6}, (U8)M_SM_QOS_SDU_ERR_1E_6},
  {       0UL, {(U16)0,(U16)0}, (U8)M_SM_QOS_SDU_ERR_SUB}
};

/*
 * Conversion table for 1/n in T_ratio.
 * Contains 1/n * 1E9 entries used for scaling.
 */
/*@checked@*/
static const U32 sm_qos_ratio_table[10] = {
           0UL,
  1000000000UL,  /* 1E9 / 1 */
   500000000UL,  /* 1E9 / 2 */
   333333333UL,  /* 1E9 / 3 */
   250000000UL,  /* 1E9 / 4 */
   200000000UL,  /* 1E9 / 5 */
   166666667UL,  /* 1E9 / 6 */
   142857143UL,  /* 1E9 / 7 */
   125000000UL,  /* 1E9 / 8 */
   111111111UL   /* 1E9 / 9 */
};

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

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

/* Conversion function: ratio to integer */
static U32 sm_qos_ratio_to_U32(U8 ratio_mant, U8 ratio_exp)
     /*@globals sm_qos_ratio_table@*/
{
  U32   value;
  int   count;

  /* Error checks */
  if (ratio_exp > (U8)9)
  {
    (void)TRACE_ERROR( "QoS ratio exponent parameter out of bounds!" );
    return 0;
  } else if (ratio_mant > (U8)9) {
    (void)TRACE_ERROR( "QoS ratio mantissa parameter out of bounds!" );
    return 0;
  } else if (ratio_exp == (U8)9 && ratio_mant < (U8)5) {
    (void)TRACE_ERROR( "QoS ratio parameter exceeds data type range!" );
    return 0;
  } else {
    for (count = 9-(int)ratio_exp, value = 1UL; count > 0; count--) {
      value *= 10;
    }
    /* Get real value by rounding 1/x and dividing by 10^(9-ratio_exp) */
    value = (sm_qos_ratio_table[(U16)ratio_mant] + (value >> 1)) / value;

    return value;
  }
}

static U8 sm_qos_ratio_to_r99aim(const struct qos_from_to *table,
                                 U8 ratio_mant, U8 ratio_exp)
     /*@globals sm_qos_ratio_table@*/
{
  int   index;
  U32   value;

  /* Check for zero / subscribed parameter */
  if (ratio_mant == (U8)0)
  {
    return (U8)M_SM_QOS_BER_SUB;
  }

  value = sm_qos_ratio_to_U32(ratio_mant, ratio_exp);

  /* Find the smallest matching table entry that is >= value */
  for (index = 0;
       table[index].integer != 0 && table[index].integer < value;
       index++)
    {};

  return (table[index].packed);
}

static void sm_qos_r99aim_to_ratio(const struct qos_from_to *table,
                                   U8 packed_ratio,
                                   /*@out@*/U8 *ratio_mant,
                                   /*@out@*/U8 *ratio_exp)
     /*@modifies *ratio_mant, *ratio_exp@*/
{
  int   index;

  for (index = 0;
       table[index].integer != 0 && packed_ratio != table[index].packed;
       index++)
    {};

  *ratio_mant   = (U8)table[index].ratio.ratio_mant;
  *ratio_exp    = (U8)table[index].ratio.ratio_exp;
}

static U32 sm_qos_r99aim_ratio_to_U32(const struct qos_from_to *table,
                                      U8 packed_ratio)
{
  int   index;

  for (index = 0;
       table[index].integer != 0 && packed_ratio != table[index].packed;
       index++)
    {};

  return (table[index].integer);
}

/* Helper function for converting "human readable" bit-rate values into
 * packed 3G air interface equvalents.
 *
 * Parameters:  Integer representation of the bit-rate
 * Returns:     Packed bit-rate value
 *
 * Conversion rules according to [3G 24.008, sec 11.5.6.5]
 */

static U8 sm_qos_bitrate_to_r99aim(U16 bitrate) /*@*/
{
  U8    result = (U8)0;

  if (bitrate < (U16)64) {
    /* 1 - 63 kbps map to values 1 - 63 @ 1kbps increments*/
    result = (U8)bitrate;
  } else if (bitrate < (U16)576) {
    /* 64 - 568 kbps map to values 64 - 127 @ 8kbps increments.
     * Values not on boundaries are rounded up.
     * Formula: result = ((bitrate + 7) / 8) + (64 - (64 / 8)) */
    result = (U8)((U16)(bitrate + ((64 * 8) - 64 + 7)) >> 3);
  } else if (bitrate < (U16)8640) {
    /* 576 - 8640 kbps map to values 128 - 254 @ 64kbps increments.
     * Values not on boundaries are rounded up.
     * Formula: result = ((bitrate + 63) / 64) + (128 - (576 / 64)) */
    result = (U8)((U16)(bitrate + ((128 * 64) - 576 + 63)) >> 6);
  } else {
    /* Error */
    (void)TRACE_EVENT( "Warning: Bitrate parameter out of bounds! Truncated..." );
    result = (U8)254;
  }
  return result;
}

/* Helper function for converting packed 3G air interface bit-rate values
 * into "human readable" equvalents.
 *
 * Parameters:  Packed bit-rate value
 * Returns:     Integer representation of the bit-rate
 *
 * Conversion rules according to [3G 24.008, sec 11.5.6.5]
 */

static U16 sm_qos_r99aim_to_bitrate(U8 packed_bitrate) /*@*/
{
  U16   bitrate = 0;

  if (packed_bitrate < (U8)64) {
    bitrate = (U16)packed_bitrate;
  } else if (packed_bitrate < (U8)128) {
    bitrate = (U16)((packed_bitrate - (U8)56) << 3);
  } else if (packed_bitrate < (U8)255) {
    bitrate = (U16)((packed_bitrate - (U8)119) << 6);
  } else {
    bitrate = 0;
  }
  return bitrate;
}

/* Helper function for converting "human readable" max SDU size values into
 * packed 3G air interface equvalents.
 *
 * Parameters:  Integer representation of the SDU size
 * Returns:     Packed SDU size value
 *
 * Conversion rules according to [3G 24.008, sec 11.5.6.5]
 */

static U8 sm_qos_max_sdu_to_r99aim(U16 sdu_size) /*@*/
{
  U8    result = (U8)0;

  if (sdu_size == (U16)PS_MAX_SDU_SUB) {
    result = (U8)PS_MAX_SDU_SUB;
  } else if (sdu_size <= (U16)1500) {/* Round up to nearest multiple of 10 and divide by 10. */
    result = (U8)((sdu_size + 9) / 10);
  } else if (sdu_size <= (U16)1502) {/* Round 1501-1502 to 1502. */
    result = (U8)PS_MAX_SDU_1502;
  } else if (sdu_size <= (U16)1510) {/* Round 1503-1510 to 1510. */
    result = (U8)PS_MAX_SDU_1510;
  } else if (sdu_size <= (U16)1520) {/* Round 1511-1520 to 1520. */
    result = (U8)PS_MAX_SDU_1520;
  } else {                      /* > 1520: We are forgiving and just truncate to 1520 */
    result = (U8)PS_MAX_SDU_1520;
    (void)TRACE_EVENT( "Warning: Max SDU size specified > 1520! Truncated..." );
  }
  return result;
}

/*
 * Helper function for converting packed 3G air interface max SDU size values
 * into "human readable" equvalents.
 *
 * Parameters:  Packed max SDU value
 * Returns:     Integer representation of the SDU size
 *
 * Conversion rules according to [3G 24.008, sec 11.5.6.5]
 */

static U16 sm_qos_r99aim_to_max_sdu(U8 packed_sdu_size) /*@*/
{
  U16   result = 0;

  if (packed_sdu_size < (U8)PS_MAX_SDU_1502) {
    result = (U16)packed_sdu_size * (U16)10;
  } else if (packed_sdu_size == (U8)PS_MAX_SDU_1502) {
    result = (U16)1502;
  } else if (packed_sdu_size == (U8)PS_MAX_SDU_1510) {
    result = (U16)1510;
  } else if (packed_sdu_size == (U8)PS_MAX_SDU_1520) {
    result = (U16)1520;
  } else {                      /* Error handling ? */
    result = (U16)1520;
    (void)TRACE_EVENT( "Warning: Packed max SDU size value > 153 (1520 octets)!  Truncated...");
  }
  return result;
}

/*
 * Helper function for converting "human readable" transfer delay values
 * into packed 3G air interface equvalents.
 *
 * Parameters:  Integer representation of the transfer delay
 * Returns:     Packed transfer delay value
 *
 * Conversion rules according to [3G 24.008, sec 11.5.6.5]
 */

static U8 sm_qos_xfer_delay_to_r99aim(U16 xfer_delay) /*@*/
{
  U8    result = (U8)0;

  if (xfer_delay == 0) {
    result = (U8)0;
  } else if (xfer_delay < (U16)10) { /* Make sure low values do not map to SUBSCRIBED */
    result = (U8)1;
  } else if (xfer_delay <= (U16)150) {
    /* 10 - 150ms map to values 1 - 15 @ 10ms increments. We round down. */
    result = (U8)(xfer_delay / 10);
  } else if (xfer_delay <= (U16)1000) {
    /* 200 - 950ms map to values 16 - 31 @ 50ms increments.
     * Values not on boundaries are rounded down.
     * Formula: result = (xfer_delay / 50) + (16 - (200 / 50)) */
    result = (U8)((xfer_delay + ((16 * 50) - 200)) / 50);
  } else if (xfer_delay <= (U16)4000) {
    /* 1000 - 4000ms map to values 32-62 @ 100ms increments.
     * Values not on boundaries are rounded down.
     * Formula: result = (xfer_delay / 100) + (32 - (1000 / 100)) */
    result = (U8)((xfer_delay + ((32 * 100) - 1000)) / 100);
  } else {
    /* Error */
    (void)TRACE_EVENT( "Warning: Transfer delay out of bounds! Truncated..." );
    result = (U8)62;
  }
  return result;
}

/*
 * Helper function for converting packed 3G air interface transfer delay values
 * into "human readable" equvalents.
 *
 * Parameters:  Packed transfer delay value
 * Returns:     Integer representation of the transfer delay
 *
 * Conversion rules according to [3G 24.008, sec 11.5.6.5]
 */

static U16 sm_qos_r99aim_to_xfer_delay(U8 packed_xfer_delay) /*@*/
{
  U16   result = 0;

  if (packed_xfer_delay == (U8)0) {
    result = (U16)PS_XFER_DELAY_SUB;
  } else if (packed_xfer_delay < (U8)16) {
    result = (U16)packed_xfer_delay * (U16)10;
  } else if (packed_xfer_delay < (U8)32) {
    result = ((U16)packed_xfer_delay - (U16)12) * (U16)50;
  } else if (packed_xfer_delay < (U8)63) {
    result = ((U16)packed_xfer_delay - (U16)22) * (U16)100;
  } else {
    result = (U16)4000;
    (void)TRACE_EVENT( "Warning: R99 transfer delay parameter out of bounds (== 63)! Truncated..." );
  }

  return result;
}

/*
 * Helper function for converting packed 3G air interface traffic class values
 * into AT command equivalents as per [3G 27.007].
 *
 * Parameters:  Packed traffic class value
 * Returns:     AT command representation of the same traffic class value
 */
static U8 sm_qos_r99aim_to_tc(U8 packed_tc) /*@*/
{
  switch (packed_tc) {
  case M_SM_QOS_TC_CONV:          return (U8)PS_TC_CONV;
  case M_SM_QOS_TC_STREAM:        return (U8)PS_TC_STREAM;
  case M_SM_QOS_TC_INTER:         return (U8)PS_TC_INTER;
  case M_SM_QOS_TC_BG:            return (U8)PS_TC_BG;
  default:
    (void)TRACE_EVENT_P1("Warning: AIM traffic class '%d' out of bounds! Defaulting to SUBSCRIBED...", packed_tc);
    return (U8)PS_TC_SUB;
  }
}

/*
 * Helper function for converting AT command traffic class values
 * into packed 3G air interface equivalents.
 *
 * Parameters:  AT command traffic class value
 * Returns:     3G AIM representation of the same traffic class value
 */
static U8 sm_qos_tc_to_r99aim(U8 tc) /*@*/
{
  switch (tc) {
  case PS_TC_CONV:               return (U8)M_SM_QOS_TC_CONV;
  case PS_TC_STREAM:             return (U8)M_SM_QOS_TC_STREAM;
  case PS_TC_INTER:              return (U8)M_SM_QOS_TC_INTER;
  case PS_TC_BG:                 return (U8)M_SM_QOS_TC_BG;
  case PS_TC_SUB:                return (U8)M_SM_QOS_TC_SUB;
  default:
    (void)TRACE_EVENT_P1("ERROR: QoS traffic class '%d' out of bounds! Defaulting to SUBSCRIBED, but expect an imminent crash!", tc);
    return (U8)M_SM_QOS_TC_SUB;
  }
}

/*
 * Helper function for converting packed 3G air interface delivery order
 * parameter values into AT command equivalents as per [3G 27.007].
 *
 * Parameters:  Packed delivery order parameter
 * Returns:     AT command representation of the same delivery order value
 */
static U8 sm_qos_r99aim_to_order(U8 packed_order) /*@*/
{
  switch (packed_order) {
  case M_SM_QOS_ORDER_NO:         return (U8)PS_ORDER_NO;
  case M_SM_QOS_ORDER_YES:        return (U8)PS_ORDER_YES;
  default:
    (void)TRACE_EVENT_P1("Warning: AIM delivery order parameter '%d' out of bounds! Defaulting to SUBSCRIBED...", packed_order);
    return (U8)PS_ORDER_SUB;
  }
}

/*
 * Helper function for converting AT command delivery order parameter values
 * into packed 3G air interface equivalents.
 *
 * Parameters:  AT command delivery order parameter
 * Returns:     Packed 3G AIM representation of the same delivery order value
 */
static U8 sm_qos_order_to_r99aim(U8 order) /*@*/
{
  switch (order) {
  case PS_ORDER_NO:              return (U8)M_SM_QOS_ORDER_NO;
  case PS_ORDER_YES:             return (U8)M_SM_QOS_ORDER_YES;
  case PS_ORDER_SUB:             return (U8)M_SM_QOS_ORDER_SUB;
  default:
    (void)TRACE_EVENT_P1("Warning: Delivery order parameter '%d' out of bounds! Defaulting to SUBSCRIBED...", order);
    return (U8)M_SM_QOS_ORDER_SUB;
  }
}

/*
 * Helper function for converting packed 3G air interface "delivery of
 * erroneous SDUs" parameter values into AT command equivalents as per
 * [3G 27.007].
 *
 * Parameters:  Packed delivery parameter
 * Returns:     AT command representation of the same delivery parameter
 */
static U8 sm_qos_r99aim_to_del_err_sdu(U8 packed_del_err_sdu) /*@*/
{
  switch (packed_del_err_sdu) {
  case M_SM_QOS_DEL_ERR_NO:       return (U8)PS_DEL_ERR_NO;
  case M_SM_QOS_DEL_ERR_YES:      return (U8)PS_DEL_ERR_YES;
  case M_SM_QOS_DEL_ERR_NODETECT: return (U8)PS_DEL_ERR_NODETECT;
  default:
    (void)TRACE_EVENT_P1("Warning: AIM delivery of err SDU parameter '%d' out of bounds! Defaulting to SUBSCRIBED", packed_del_err_sdu);
    return (U8)PS_DEL_ERR_SUB;
  }
}

/*
 * Helper function for converting AT command "delivery of erroneous SDUs"
 * parameter values into packed 3G air interface equivalents.
 *
 * Parameters:  AT command delivery parameter
 * Returns:     Packed 3G AIM representation of the same delivery value
 */
static U8 sm_qos_del_err_sdu_to_r99aim(U8 del_err_sdu) /*@*/
{
  switch (del_err_sdu) {
  case PS_DEL_ERR_NO:            return (U8)M_SM_QOS_DEL_ERR_NO;
  case PS_DEL_ERR_YES:           return (U8)M_SM_QOS_DEL_ERR_YES;
  case PS_DEL_ERR_NODETECT:      return (U8)M_SM_QOS_DEL_ERR_NODETECT;
  case PS_DEL_ERR_SUB:           return (U8)M_SM_QOS_DEL_ERR_SUB;
  default:
    (void)TRACE_EVENT_P1("Warning: Delivery of err SDU parameter '%d' out of bounds! Defaulting to SUBSCRIBED...", del_err_sdu);
    return (U8)M_SM_QOS_DEL_ERR_SUB;
  }
}

/*
 * Function for converting a R97 QoS parameter set in SM internal representation
 * into a 3G air message QoS parameter set.
 *
 * Conversion rules according to [3G 24.008, sec 10.5.6.5]
 *
 * Parameters:  Originating R97 SM QoS parameter struct
 *              Destination R97/R99 3G QoS parameter struct (overwritten in this function)
 */

static void sm_qos_convert_r97_to_aim(/*@in@*/ T_PS_qos_r97  *src_qos_r97,
                                      /*@out@*/T_M_SM_qos     *dst_qos)
     /*@modifies dst_qos->qos_r97@*/
{
#ifdef DEBUG_VERBOSE
  (void)TRACE_FUNCTION( "sm_qos_convert_r97_to_aim" );
#endif

  dst_qos->qos_r97.delay       = src_qos_r97->delay;
  dst_qos->qos_r97.reliability = src_qos_r97->relclass;
  dst_qos->qos_r97.peak        = src_qos_r97->peak;
  dst_qos->qos_r97.precedence  = src_qos_r97->preced;
  dst_qos->qos_r97.mean        = src_qos_r97->mean;
}

static void sm_qos_convert_r97aim_to_r97(/*@in@*/ T_M_SM_qos_r97 *src_qos,
                                         /*@out@*/T_PS_qos_r97  *dst_qos_r97)
     /*@modifies dst_qos_r97@*/
{
#ifdef DEBUG_VERBOSE
  (void)TRACE_FUNCTION( "sm_qos_convert_r97aim_to_r97" );
#endif

  dst_qos_r97->delay           = src_qos->delay;
  dst_qos_r97->relclass        = src_qos->reliability;
  dst_qos_r97->peak            = src_qos->peak;
  dst_qos_r97->preced          = src_qos->precedence;
  dst_qos_r97->mean            = src_qos->mean;
}

/*
 * Function for converting a R99 QoS parameter set in SM internal representation
 * into a 3G air message QoS parameter set.
 *
 * Conversion rules according to [3G 24.008, sec 10.5.6.5]
 *
 * Parameters:  Originating R99 SM QoS parameter struct
 *              Destination R99 3G QoS parameter struct (overwritten in this function)
 */

static void sm_qos_convert_r99_to_aim(/*@in@*/ T_PS_qos_r99 *src_qos_r99,
                                      /*@out@*/T_M_SM_qos    *dst_qos)
 /*@globals sm_qos_ber_table, sm_qos_sdu_err_ratio_table, sm_qos_ratio_table@*/
 /*@modifies dst_qos->qos_r99@*/
{
#ifdef DEBUG_VERBOSE
  (void)TRACE_FUNCTION( "sm_qos_convert_r99_to_aim" );
#endif

  /* Traffic class */
  dst_qos->qos_r99.tc          = sm_qos_tc_to_r99aim(src_qos_r99->tc);

  /* Delivery order */
  dst_qos->qos_r99.order       = sm_qos_order_to_r99aim(src_qos_r99->order);

  /* Delivery of erroneous SDUs */
  dst_qos->qos_r99.del_err_sdu = sm_qos_del_err_sdu_to_r99aim(src_qos_r99->del_err_sdu);

  /* Max SDU size */
  dst_qos->qos_r99.max_sdu     = sm_qos_max_sdu_to_r99aim(src_qos_r99->max_sdu);

  /* Max uplink bit-rate */
  dst_qos->qos_r99.max_br_ul   = sm_qos_bitrate_to_r99aim(src_qos_r99->max_rate_ul);

  /* Max downlink bit-rate */
  dst_qos->qos_r99.max_br_dl   = sm_qos_bitrate_to_r99aim(src_qos_r99->max_rate_dl);

  /* Residual BER */
  dst_qos->qos_r99.ber         = sm_qos_ratio_to_r99aim(sm_qos_ber_table,
                                   src_qos_r99->ber.ratio_mant,
                                   src_qos_r99->ber.ratio_exp);

  /* SDU error ratio */
  dst_qos->qos_r99.sdu_err_ratio = sm_qos_ratio_to_r99aim(sm_qos_sdu_err_ratio_table,
                                   src_qos_r99->sdu_err_ratio.ratio_mant,
                                   src_qos_r99->sdu_err_ratio.ratio_exp);

  /* Transfer delay */
  dst_qos->qos_r99.xfer_delay  = sm_qos_xfer_delay_to_r99aim(src_qos_r99->xfer_delay);

  /* Traffic handling priority */
  dst_qos->qos_r99.handling_pri= src_qos_r99->handling_pri;

  /* Guaranteed uplink bit-rate */
  dst_qos->qos_r99.guar_br_ul  = sm_qos_bitrate_to_r99aim(src_qos_r99->guar_br_ul);

  /* Guaranteed downlink bit-rate */
  dst_qos->qos_r99.guar_br_dl  = sm_qos_bitrate_to_r99aim(src_qos_r99->guar_br_dl);
}

/*
 * Function for converting a R99 3G air message QoS parameter set into a
 * R99 QoS parameter set in SM internal representation
 *
 * Conversion rules according to [3G 24.008, sec 10.5.6.5]
 *
 * Parameters:  Originating R99 3G QoS parameter struct
 *              Destination R99 SM QoS parameter struct (overwritten in this function)
 */

static void sm_qos_convert_r99aim_to_r99(/*@in@*/ T_M_SM_qos_r99 *src_qos_r99,
                                         /*@out@*/T_PS_qos_r99  *dst_qos_r99)
 /*@globals sm_qos_ber_table, sm_qos_sdu_err_ratio_table@*/
 /*@modifies dst_qos_r99@*/
{
#ifdef DEBUG_VERBOSE
  (void)TRACE_FUNCTION( "sm_qos_convert_r99aim_to_r99" );
#endif

  /* Traffic class */
  dst_qos_r99->tc          = sm_qos_r99aim_to_tc(src_qos_r99->tc);

  /* Delivery order */
  dst_qos_r99->order       = sm_qos_r99aim_to_order(src_qos_r99->order);

  /* Delivery of erroneous SDUs */
  dst_qos_r99->del_err_sdu = sm_qos_r99aim_to_del_err_sdu(src_qos_r99->del_err_sdu);

  /* Max SDU size */
  dst_qos_r99->max_sdu     = sm_qos_r99aim_to_max_sdu(src_qos_r99->max_sdu);

  /* Max uplink bit-rate */
  dst_qos_r99->max_rate_ul = sm_qos_r99aim_to_bitrate(src_qos_r99->max_br_ul);

  /* Max downlink bit-rate */
  dst_qos_r99->max_rate_dl = sm_qos_r99aim_to_bitrate(src_qos_r99->max_br_dl);

  /* Residual BER */
  sm_qos_r99aim_to_ratio(sm_qos_ber_table, src_qos_r99->ber,
                      &dst_qos_r99->ber.ratio_mant,
                      &dst_qos_r99->ber.ratio_exp);

  /* SDU error ratio */
  sm_qos_r99aim_to_ratio(sm_qos_sdu_err_ratio_table, src_qos_r99->sdu_err_ratio,
                      &dst_qos_r99->sdu_err_ratio.ratio_mant,
                      &dst_qos_r99->sdu_err_ratio.ratio_exp);

  /* Transfer delay */
  dst_qos_r99->xfer_delay  = sm_qos_r99aim_to_xfer_delay(src_qos_r99->xfer_delay);

  /* Traffic handling priority */
  dst_qos_r99->handling_pri = src_qos_r99->handling_pri;

  /* Guaranteed uplink bit-rate */
  dst_qos_r99->guar_br_ul  = sm_qos_r99aim_to_bitrate(src_qos_r99->guar_br_ul);

  /* Guaranteed downlink bit-rate */
  dst_qos_r99->guar_br_dl  = sm_qos_r99aim_to_bitrate(src_qos_r99->guar_br_dl);
}

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

/*
 * Function for extracting the traffic class element stored for
 * the given context.
 */
U8 sm_qos_get_traffic_class(struct T_SM_CONTEXT_DATA *context)
{
   /*lint -e613 (Possible use of null pointer 'context' in left argument to operator '->') */
  TRACE_ASSERT(context != NULL);
 
  if (context->requested_qos.ctrl_qos == PS_is_R97) {
    T_PS_qos_r99 temp_qos;

    (void)cl_qos_convert_r97_to_r99(&context->requested_qos.qos.qos_r97,
                                        &temp_qos);

    return temp_qos.tc;
  }
  else if (context->requested_qos.ctrl_qos == PS_is_R99) {
    return context->requested_qos.qos.qos_r99.tc;
  }
  else {
    /* Default to SUBSCRIBED */
    return (U8)PS_TC_SUB;
  }
}

/*
+------------------------------------------------------------------------------
| Function    : sm_qos_convert_to_aim
+------------------------------------------------------------------------------
| Description : Function for converting a QoS parameter set in internal SM
|               representation into an air interface QoS parameter set.
|               Handles both R97 and R99 types, skipping R99 elements if
|               network is R97.
|
| Parameters  : src_qos          - Source QoS (internal representation)
|               dst_qos          - Destination air interface QoS structure
|               release          - Core network release (R97/R99)
+------------------------------------------------------------------------------
*/
void sm_qos_convert_to_aim(/*@in@*/ T_SM_qos   *src_qos,
                           /*@out@*/T_M_SM_qos *dst_qos,
                           T_PS_sgsn_rel        release)
 /*@globals sm_qos_ber_table, sm_qos_sdu_err_ratio_table, sm_qos_ratio_table@*/
{
  
#ifdef DEBUG_VERBOSE
  (void)TRACE_FUNCTION("sm_qos_convert_to_aim");
#endif

  /* Is SGSN release = pre-R99 (or R98 and older) ? */
  if (release == PS_SGSN_98_OLDER) {
    if (src_qos->ctrl_qos == PS_is_R97) {
      sm_qos_convert_r97_to_aim(&src_qos->qos.qos_r97, dst_qos);
    } else if (src_qos->ctrl_qos == PS_is_R99) {
      /* Convert R99 to R97 values */
      T_PS_qos_r97  tmp_qos_r97;
      cl_qos_convert_r99_to_r97(&src_qos->qos.qos_r99, &tmp_qos_r97);
      sm_qos_convert_r97_to_aim(&tmp_qos_r97, dst_qos);
    } else {
      (void)TRACE_ERROR( "Invalid ctrl_qos value!" );
    }
    /* Do not include R99 QoS elements in air interface message */
    dst_qos->v_qos_r99 = (U8)FALSE;
    dst_qos->tlv_len   = (U8)M_SM_SIZE_R97_QOS;
  } else if (release == PS_SGSN_99_ONWARDS) {
    if (src_qos->ctrl_qos == PS_is_R97) {
      T_PS_qos_r99  tmp_qos;
      /* Convert R97 to R99 struct */
      (void)cl_qos_convert_r97_to_r99(&src_qos->qos.qos_r97, &tmp_qos);
      /* Include both R97 and R99 QoS info in AIM */
      sm_qos_convert_r97_to_aim(&src_qos->qos.qos_r97, dst_qos);
      sm_qos_convert_r99_to_aim(&tmp_qos, dst_qos);
    } else if (src_qos->ctrl_qos == PS_is_R99) {
      T_PS_qos_r97  tmp_qos_r97;
      /* Fill in R99 AIM fields */
      sm_qos_convert_r99_to_aim(&src_qos->qos.qos_r99, dst_qos);
      /* [3G 24.008] says to always include R97/R98 parameters in QoS IE */
      cl_qos_convert_r99_to_r97(&src_qos->qos.qos_r99, &tmp_qos_r97);
      sm_qos_convert_r97_to_aim(&tmp_qos_r97, dst_qos);
    } else {
      (void)TRACE_ERROR( "Invalid ctrl_qos value!" );
    }
    /* Include R99 QoS elements in air interface message */
    dst_qos->v_qos_r99 = (U8)TRUE;
    dst_qos->tlv_len   = (U8)M_SM_SIZE_R99_QOS;
  } else {
    (void)TRACE_EVENT_P1("ERROR: Invalid network release union controller %d!",
                         release);
  }
}

/*
+------------------------------------------------------------------------------
| Function    : sm_qos_assign_from_aim
+------------------------------------------------------------------------------
| Description : Function for converting an air interface QoS parameter set
|               to internal SM representation.  Handles both R97 and R99 types,
|               and sets the destination QoS union controller to the R97/R99
|               type of the air interface message.
|
| Parameters  : dst_qos          - Destination QoS (internal representation)
|               src_qos          - Source air interface QoS structure
+------------------------------------------------------------------------------
*/
void sm_qos_assign_from_aim(/*@out@*/T_SM_qos   *dst_qos,
                            /*@in@*/ T_M_SM_qos *src_qos)
 /*@globals sm_qos_ber_table, sm_qos_sdu_err_ratio_table@*/
{
#ifdef DEBUG_VERBOSE
  (void)TRACE_FUNCTION( "sm_qos_assign_from_aim");
#endif

  /* Does QoS include R99 elements? */
  if (src_qos->v_qos_r99 == (U8)FALSE) {
    /* FALSE == No R99 elements present; Set type to R97 */
    dst_qos->ctrl_qos = PS_is_R97;
    sm_qos_convert_r97aim_to_r97(&src_qos->qos_r97, &dst_qos->qos.qos_r97);
  } else {
    /* TRUE == R99 elements present; Set type to R99 */
    dst_qos->ctrl_qos = PS_is_R99;
    sm_qos_convert_r99aim_to_r99(&src_qos->qos_r99, &dst_qos->qos.qos_r99);
  }
}

/*
+------------------------------------------------------------------------------
| Function    : sm_qos_compare_r97_lt_r97
+------------------------------------------------------------------------------
| Description : Compares an R97 QoS structure with another (minimum) R97 QoS
|               structure in internal SM representation.  Returns TRUE if
|               the supplied QoS is *less than* minimum QoS.
| Parameters  : cmp_qos_r97      - comparison QoS structure
|               min_qos_r97      - minimum QoS structure
+------------------------------------------------------------------------------
*/
/* NOTE:  Removed.  sm-to-sm comparisons only occur during MO modify
 * procedures, which are not possible in R97.*/
#if 0
static BOOL sm_qos_compare_r97_lt_r97(/*@in@*/T_PS_qos_r97 *cmp_qos_r97,
                                      /*@in@*/T_PS_qos_r97 *min_qos_r97)
{
  (void)TRACE_FUNCTION( "sm_qos_compare_r97aim_lt_r97");

  if (min_qos_r97->delay       != (U8)NAS_DELAY_SUB &&
      cmp_qos_r97->delay       > min_qos_r97->delay)
  {
    (void)TRACE_EVENT_P2("R97 DELAY parameter insufficient: %d > %d (min_qos)",
                         cmp_qos_r97->delay, min_qos_r97->delay);
    return TRUE;
  }
  if (min_qos_r97->relclass    != (U8)NAS_RELCLASS_SUB &&
      cmp_qos_r97->relclass    > min_qos_r97->relclass)
  {
    (void)TRACE_EVENT_P2("R97 RELIABILITY CLASS parameter insufficient: "
                         "%d > %d (min_qos)",
                         cmp_qos_r97->relclass, min_qos_r97->relclass);
    return TRUE;
  }
  if (min_qos_r97->peak        != (U8)NAS_PEAK_SUB &&
      cmp_qos_r97->peak        < min_qos_r97->peak)
  {
    (void)TRACE_EVENT_P2("R97 PEAK BITRATE parameter insufficient: "
                         "%d < %d (min_qos)",
                         cmp_qos_r97->peak, min_qos_r97->peak);
    return TRUE;
  }
  if (min_qos_r97->preced      != (U8)NAS_PRECED_SUB &&
      cmp_qos_r97->preced      > min_qos_r97->preced)
  {
    (void)TRACE_EVENT_P2("R97 PRECEDENCE CLASS parameter insufficient: "
                         "%d > %d (min_qos)",
                         cmp_qos_r97->preced, min_qos_r97->preced);
    return TRUE;
  }
  if (min_qos_r97->mean        != (U8)NAS_MEAN_SUB &&
      cmp_qos_r97->mean        < min_qos_r97->mean)
  {
    (void)TRACE_EVENT_P2("R97 MEAN BITRATE parameter insufficient: "
                         "%d < %d (min_qos)",
                         cmp_qos_r97->mean, min_qos_r97->mean);
    return TRUE;
  }
  return FALSE;
}
#endif

/*
+------------------------------------------------------------------------------
| Function    : sm_qos_compare_r99_lt_r99
+------------------------------------------------------------------------------
| Description : Compares an R99 QoS structure with a (minimum) R99 QoS
|               structure in internal SM representation.  Returns TRUE if
|               comparison QoS is *less than* minimum QoS.
| Parameters  : cmp_qos_r99      - air interface QoS structure
|               min_qos_r99      - minimum QoS structure
+------------------------------------------------------------------------------
*/
static BOOL sm_qos_compare_r99_lt_r99(/*@in@*/T_PS_qos_r99 *cmp_qos_r99,
                                      /*@in@*/T_PS_qos_r99 *min_qos_r99)
{
  T_PS_tc  traffic_class = (T_PS_tc)cmp_qos_r99->tc;
#ifdef DEBUG_VERBOSE
  (void)TRACE_FUNCTION( "sm_qos_compare_r99_lt_r99");
#endif

  /* Traffic class */
  if ((min_qos_r99->tc != (U8)PS_TC_SUB) &&
      (cmp_qos_r99->tc > min_qos_r99->tc))
  {
    (void)TRACE_EVENT_P2("R99 TRAFFIC CLASS parameter insufficient: "
                         "%d > %d (min_qos)",
                         cmp_qos_r99->tc, min_qos_r99->tc);
    return TRUE;
  }

  /* Delivery order */
  if ((min_qos_r99->order != (U8)PS_ORDER_SUB) &&
      (cmp_qos_r99->order != min_qos_r99->order))
  {
    (void)TRACE_EVENT_P2("R99 DELIVERY ORDER parameter insufficient: "
                         "%d != %d (min_qos)",
                         cmp_qos_r99->order, min_qos_r99->order);
    return TRUE;
  }

  /* Delivery of erroneous SDUs */
  if ((min_qos_r99->del_err_sdu != (U8)PS_DEL_ERR_SUB) &&
      (cmp_qos_r99->del_err_sdu != min_qos_r99->del_err_sdu))
  {
    (void)TRACE_EVENT_P2("R99 DELIVERY of ERRONEUOS SDUs parameter "
                         "insufficient: %d != %d (min_qos)",
                         cmp_qos_r99->del_err_sdu, min_qos_r99->del_err_sdu);
    return TRUE;
  }

  /* Max SDU size */
  if ((min_qos_r99->max_sdu != (U16)PS_MAX_SDU_SUB) &&
      (cmp_qos_r99->max_sdu < min_qos_r99->max_sdu))
  {
    (void)TRACE_EVENT_P2("R99 MAX SDU SIZE parameter insufficient: "
                         "%d < %d (min_qos)",
                         cmp_qos_r99->max_sdu, min_qos_r99->max_sdu);
    return TRUE;
  }

  /* Max uplink bit-rate */
  if ((min_qos_r99->max_rate_ul != (U16)PS_MAX_BR_UL_SUB) &&
      (cmp_qos_r99->max_rate_ul < min_qos_r99->max_rate_ul))
  {
    (void)TRACE_EVENT_P2("R99 MAX UPLINK BITRATE parameter insufficient: "
                         "%d < %d (min_qos)",
                         cmp_qos_r99->max_rate_ul, min_qos_r99->max_rate_ul);
    return TRUE;
  }

  /* Max downlink bit-rate */
  if ((min_qos_r99->max_rate_dl != (U16)PS_MAX_BR_DL_SUB) &&
      (cmp_qos_r99->max_rate_dl < min_qos_r99->max_rate_dl))
  {
    (void)TRACE_EVENT_P2("R99 MAX DOWNLINK BITRATE parameter insufficient: "
                         "%d < %d (min_qos)",
                         cmp_qos_r99->max_rate_dl, min_qos_r99->max_rate_dl);
    return TRUE;
  }

  /* Residual BER */
  if ((min_qos_r99->ber.ratio_exp != (U8)0
       && min_qos_r99->ber.ratio_mant != (U8)0) &&
      (sm_qos_ratio_to_U32(cmp_qos_r99->ber.ratio_mant,
                           cmp_qos_r99->ber.ratio_exp) <
       sm_qos_ratio_to_U32(min_qos_r99->ber.ratio_mant,
                           min_qos_r99->ber.ratio_exp)))
  {
    (void)TRACE_EVENT_P4("R99 RESIDUAL BIT ERROR RATE parameter insufficient: "
                         "%dE-%d < %dE-%d (min_qos)",
                         cmp_qos_r99->ber.ratio_mant,
                         cmp_qos_r99->ber.ratio_exp,
                         min_qos_r99->ber.ratio_mant,
                         min_qos_r99->ber.ratio_exp);
    return TRUE;
  }

  /* SDU error ratio */
  if ((min_qos_r99->sdu_err_ratio.ratio_exp != (U8)0 &&
       min_qos_r99->sdu_err_ratio.ratio_mant != (U8)0) &&
      (sm_qos_ratio_to_U32(cmp_qos_r99->sdu_err_ratio.ratio_mant,
                           cmp_qos_r99->sdu_err_ratio.ratio_exp) <
       sm_qos_ratio_to_U32(min_qos_r99->sdu_err_ratio.ratio_mant,
                           min_qos_r99->sdu_err_ratio.ratio_exp)))
  {
    (void)TRACE_EVENT_P4("R99 SDU ERROR RATIO parameter insufficient: "
                         "%dE-%d < %dE-%d (min_qos)",
                         cmp_qos_r99->sdu_err_ratio.ratio_mant,
                         cmp_qos_r99->sdu_err_ratio.ratio_exp,
                         min_qos_r99->sdu_err_ratio.ratio_mant,
                         min_qos_r99->sdu_err_ratio.ratio_exp);
    return TRUE;
  }

  /* Transfer delay - Note! Only for real-time traffic class traffic! */
  if ((traffic_class == PS_TC_CONV || traffic_class == PS_TC_STREAM) &&
      (min_qos_r99->xfer_delay != (U16)PS_XFER_DELAY_SUB) &&
      (cmp_qos_r99->xfer_delay > min_qos_r99->xfer_delay))
  {
    (void)TRACE_EVENT_P2("R99 TRANSFER DELAY parameter insufficient: "
                         "%d > %d (min_qos)",
                         cmp_qos_r99->xfer_delay, min_qos_r99->xfer_delay);
    return TRUE;
  }

  /* Traffic handling priority - Note: Only interactive traffic class! */
  if ((traffic_class == PS_TC_INTER) &&
      (min_qos_r99->handling_pri != (U8)PS_HANDLING_PRI_SUB) &&
      (cmp_qos_r99->handling_pri > min_qos_r99->handling_pri))
  {
    (void)TRACE_EVENT_P2("R99 TRANSFER HANDLING PRIORITY parameter "
                         "insufficient: %d > %d (min_qos)",
                         cmp_qos_r99->handling_pri, min_qos_r99->handling_pri);
    return TRUE;
  }

  /* Guaranteed uplink bit-rate - Note: Only for real-time traffic class traffic! */
  if ((traffic_class == PS_TC_CONV || traffic_class == PS_TC_STREAM) &&
      (min_qos_r99->guar_br_ul != (U16)PS_GUAR_BR_UL_SUB) &&
      (cmp_qos_r99->guar_br_ul < min_qos_r99->guar_br_ul))
  {
    (void)TRACE_EVENT_P2("R99 GUARANTEED UPLINK BITRATE parameter "
                         "insufficient: %d < %d (min_qos)",
                         cmp_qos_r99->guar_br_ul, min_qos_r99->guar_br_ul);
    return TRUE;
  }

  /* Guaranteed downlink bit-rate  - Note: Only for real-time traffic class traffic! */
  if ((traffic_class == PS_TC_CONV || traffic_class == PS_TC_STREAM) &&
      (min_qos_r99->guar_br_dl != (U16)PS_GUAR_BR_DL_SUB) &&
      (cmp_qos_r99->guar_br_dl < min_qos_r99->guar_br_dl))
  {
    (void)TRACE_EVENT_P2("R99 GUARANTEED DOWNLINK BITRATE parameter "
                         "insufficient: %d < %d (min_qos)",
                         cmp_qos_r99->guar_br_dl, min_qos_r99->guar_br_dl);
    return TRUE;
  }

  return FALSE;
}

/*
+------------------------------------------------------------------------------
| Function    : sm_qos_compare_r97aim_lt_r97
+------------------------------------------------------------------------------
| Description : Compares an R97 air interface QoS structure with an R97 QoS
|               structure in internal SM representation.  Returns TRUE if
|               AIM QoS is *less than* min QoS or *greater than* req QoS.
| Parameters  : aim_qos_r97      - air interface QoS structure
|               min_qos_r97      - minimum QoS structure
|               req_qos_r97      - requested QoS structure
+------------------------------------------------------------------------------
*/
static BOOL sm_qos_compare_r97aim_lt_r97(/*@in@*/T_M_SM_qos_r97 *aim_qos_r97,
                                         /*@in@*/T_PS_qos_r97  *min_qos_r97,
                                         /*@in@*/T_PS_qos_r97  *req_qos_r97 )
{
  (void)TRACE_FUNCTION( "sm_qos_compare_r97aim_lt_r97");

  /* Compare R97 AIM QoS with R97 parameter struct */
  if ( (min_qos_r97->delay       != (U8)PS_DELAY_SUB && 
        aim_qos_r97->delay       > min_qos_r97->delay ) ||
       (req_qos_r97->delay       != (U8)PS_DELAY_SUB && 
        aim_qos_r97->delay       < req_qos_r97->delay) )
  {
    (void)TRACE_EVENT_P4("R97 DELAY parameter insufficient: "
                          "%d > %d (min_qos) || %d < %d (req_qos)", 
                          aim_qos_r97->delay, min_qos_r97->delay, \
                          aim_qos_r97->delay, req_qos_r97->delay);
    return TRUE;
  }
  if ( (min_qos_r97->relclass    != (U8)PS_RELCLASS_SUB &&
        aim_qos_r97->reliability > min_qos_r97->relclass) || 
       (req_qos_r97->relclass    != (U8)PS_RELCLASS_SUB && 
        aim_qos_r97->reliability < req_qos_r97->relclass) && 
        /*the following line is for the TC 46.1.2.2.1.2*/
      !((aim_qos_r97->reliability ==2)  && (req_qos_r97->relclass == 5)) ) 
  {
    (void)TRACE_EVENT_P4("R97 RELIABILITY CLASS parameter insufficient: "
                         "%d > %d (min_qos) %d < %d (req_qos)",
                         aim_qos_r97->reliability, min_qos_r97->relclass, \
                         aim_qos_r97->reliability, req_qos_r97->relclass);
    return TRUE;
  }
  if ( (min_qos_r97->peak != (U8)PS_PEAK_SUB && 
        aim_qos_r97->peak <  min_qos_r97->peak) ||
       (req_qos_r97->peak != (U8)PS_PEAK_SUB && 
        aim_qos_r97->peak >  req_qos_r97->peak) )
  {
    (void)TRACE_EVENT_P4("R97 PEAK BITRATE parameter insufficient: "
                         "%d < %d (min_qos) || %d > %d (req_qos)",
                         aim_qos_r97->peak, min_qos_r97->peak, \
                         aim_qos_r97->peak, req_qos_r97->peak);
    return TRUE;
  }
  if ( (min_qos_r97->preced      != (U8)PS_PRECED_SUB &&
        aim_qos_r97->precedence  > min_qos_r97->preced) ||
       (req_qos_r97->preced      != (U8)PS_PRECED_SUB &&
        aim_qos_r97->precedence  < req_qos_r97->preced))
  {
    (void)TRACE_EVENT_P4("R97 PRECEDENCE CLASS parameter insufficient: "
                         "%d > %d (min_qos) || %d < %d (req_qos)",
                         aim_qos_r97->precedence, min_qos_r97->preced, \
                         aim_qos_r97->precedence, req_qos_r97->preced);
    return TRUE;
  }
  if ( (min_qos_r97->mean        != (U8)PS_MEAN_SUB &&
        aim_qos_r97->mean        < min_qos_r97->mean) || 
       (req_qos_r97->mean        != (U8)PS_MEAN_SUB &&
        aim_qos_r97->mean        > req_qos_r97->mean) )
  {
    (void)TRACE_EVENT_P4("R97 MEAN BITRATE parameter insufficient: "
                         "%d < %d (min_qos) || %d > %d (req_qos)",
                         aim_qos_r97->mean, min_qos_r97->mean, \
                         aim_qos_r97->mean, req_qos_r97->mean);
    return TRUE;
  }

  return FALSE;
}

/*
+------------------------------------------------------------------------------
| Function    : sm_qos_compare_r99aim_lt_r99
+------------------------------------------------------------------------------
| Description : Compares an R99 air interface QoS structure with an R99 QoS
|               structure in internal SM representation.  Returns TRUE if
|               AIM QoS is *less than* minimum QoS.
| Parameters  : aim_qos_r99      - air interface QoS structure
|               min_qos_r99      - minimum QoS structure
+------------------------------------------------------------------------------
*/
static BOOL sm_qos_compare_r99aim_lt_r99(/*@in@*/T_M_SM_qos_r99 *aim_qos_r99,
                                         /*@in@*/T_PS_qos_r99  *min_qos_r99)
{
  (void)TRACE_FUNCTION( "sm_qos_compare_r99aim_lt_r99" );

  /* Traffic class */
  if ((min_qos_r99->tc != (U8)PS_TC_SUB) &&
      (sm_qos_r99aim_to_tc(aim_qos_r99->tc) > min_qos_r99->tc))
  {
    (void)TRACE_EVENT_P2("R99 TRAFFIC CLASS parameter insufficient: "
                         "%d > %d (min_qos)",
                         sm_qos_r99aim_to_tc(aim_qos_r99->tc),
                         min_qos_r99->tc);
    return TRUE;
  }

  /* Delivery order */
  if ((min_qos_r99->order != (U8)PS_ORDER_SUB) &&
      (sm_qos_r99aim_to_order(aim_qos_r99->order) != min_qos_r99->order))
  {
    (void)TRACE_EVENT_P2("R99 DELIVERY ORDER parameter insufficient: "
                         "%d != %d (min_qos)",
                         sm_qos_r99aim_to_order(aim_qos_r99->order),
                         min_qos_r99->order);
    return TRUE;
  }

  /* Delivery of erroneous SDUs */
  if ((min_qos_r99->del_err_sdu != (U8)PS_DEL_ERR_SUB) &&
      (sm_qos_r99aim_to_del_err_sdu(aim_qos_r99->del_err_sdu) != min_qos_r99->del_err_sdu))
  {
    (void)TRACE_EVENT_P2("R99 DELIVERY of ERRONEUOS SDUs parameter "
                         "insufficient: %d != %d (min_qos)",
                         sm_qos_r99aim_to_del_err_sdu(aim_qos_r99->del_err_sdu),
                         min_qos_r99->del_err_sdu);
    return TRUE;
  }

  /* Max SDU size */
  if ((min_qos_r99->max_sdu != (U16)PS_MAX_SDU_SUB) &&
      (sm_qos_r99aim_to_max_sdu(aim_qos_r99->max_sdu) < min_qos_r99->max_sdu))
  {
    (void)TRACE_EVENT_P2("R99 MAX SDU SIZE parameter insufficient: "
                         "%d < %d (min_qos)",
                         sm_qos_r99aim_to_max_sdu(aim_qos_r99->max_sdu),
                         min_qos_r99->max_sdu);
    return TRUE;
  }

  /* Max uplink bit-rate */
  if ((min_qos_r99->max_rate_ul != (U16)PS_MAX_BR_UL_SUB) &&
      (sm_qos_r99aim_to_bitrate(aim_qos_r99->max_br_ul) < min_qos_r99->max_rate_ul))
  {
    (void)TRACE_EVENT_P2("R99 MAX UPLINK BITRATE parameter insufficient: "
                         "%d < %d (min_qos)",
                         sm_qos_r99aim_to_bitrate(aim_qos_r99->max_br_ul),
                         min_qos_r99->max_rate_ul);
    return TRUE;
  }

  /* Max downlink bit-rate */
  if ((min_qos_r99->max_rate_dl != (U16)PS_MAX_BR_DL_SUB) &&
      (sm_qos_r99aim_to_bitrate(aim_qos_r99->max_br_dl) < min_qos_r99->max_rate_dl))
  {
    (void)TRACE_EVENT_P2("R99 MAX DOWNLINK BITRATE parameter insufficient: "
                         "%d < %d (min_qos)",
                         sm_qos_r99aim_to_bitrate(aim_qos_r99->max_br_ul),
                         min_qos_r99->max_rate_dl);
    return TRUE;
  }

  /* Residual BER */
  if ((min_qos_r99->ber.ratio_exp != (U8)0
       && min_qos_r99->ber.ratio_mant != (U8)0) &&
      (sm_qos_r99aim_ratio_to_U32(sm_qos_ber_table, aim_qos_r99->ber) <
       sm_qos_ratio_to_U32(min_qos_r99->ber.ratio_mant,
                           min_qos_r99->ber.ratio_exp)))
  {
#ifdef DEBUG
    U8 ratio_exp, ratio_mant;
    sm_qos_r99aim_to_ratio(sm_qos_ber_table, aim_qos_r99->ber,
                           &ratio_mant, &ratio_exp);
    (void)TRACE_EVENT_P4("R99 RESIDUAL BIT ERROR RATE parameter insufficient: "
                         "%dE-%d < %dE-%d (min_qos)",
                         ratio_mant, ratio_exp,
                         min_qos_r99->ber.ratio_mant,
                         min_qos_r99->ber.ratio_exp);
#endif
    return TRUE;
  }

  /* SDU error ratio */
  if ((min_qos_r99->sdu_err_ratio.ratio_exp != (U8)0 &&
       min_qos_r99->sdu_err_ratio.ratio_mant != (U8)0) &&
      (sm_qos_r99aim_ratio_to_U32(sm_qos_sdu_err_ratio_table, aim_qos_r99->sdu_err_ratio) <
       sm_qos_ratio_to_U32(min_qos_r99->sdu_err_ratio.ratio_mant,
                           min_qos_r99->sdu_err_ratio.ratio_exp)))
  {
#ifdef DEBUG
    U8 ratio_exp, ratio_mant;
    sm_qos_r99aim_to_ratio(sm_qos_sdu_err_ratio_table,
                           aim_qos_r99->sdu_err_ratio,
                           &ratio_mant, &ratio_exp);
    (void)TRACE_EVENT_P4("R99 SDU ERROR RATIO parameter insufficient: "
                         "%dE-%d < %dE-%d (min_qos)",
                         ratio_mant, ratio_exp,
                         min_qos_r99->sdu_err_ratio.ratio_mant,
                         min_qos_r99->sdu_err_ratio.ratio_exp);
#endif
    return TRUE;
  }

  /* Transfer delay - Note! Only for real-time traffic class traffic! */
  if ((aim_qos_r99->tc == (U8)M_SM_QOS_TC_CONV
       || aim_qos_r99->tc == (U8)M_SM_QOS_TC_STREAM) &&
      (min_qos_r99->xfer_delay != (U16)PS_XFER_DELAY_SUB) &&
      (sm_qos_r99aim_to_xfer_delay(aim_qos_r99->xfer_delay) > min_qos_r99->xfer_delay))
  {
    (void)TRACE_EVENT_P2("R99 TRANSFER DELAY parameter insufficient: "
                         "%d > %d (min_qos)",
                         sm_qos_r99aim_to_xfer_delay(aim_qos_r99->xfer_delay),
                         min_qos_r99->xfer_delay);
    return TRUE;
  }

  /* Traffic handling priority - Note: Only interactive traffic class! */
  if ((aim_qos_r99->tc == (U8)M_SM_QOS_TC_INTER) &&
      (min_qos_r99->handling_pri != (U8)PS_HANDLING_PRI_SUB) &&
      (aim_qos_r99->handling_pri > min_qos_r99->handling_pri))
  {
    (void)TRACE_EVENT_P2("R99 TRANSFER HANDLING PRIORITY parameter "
                         "insufficient: %d > %d (min_qos)",
                         aim_qos_r99->handling_pri, min_qos_r99->handling_pri);
    return TRUE;
  }

  /* Guaranteed uplink bit-rate - Note: Only for real-time traffic class traffic! */
  if ((aim_qos_r99->tc == (U8)M_SM_QOS_TC_CONV
       || aim_qos_r99->tc == (U8)M_SM_QOS_TC_STREAM) &&
      (min_qos_r99->guar_br_ul != (U16)PS_GUAR_BR_UL_SUB) &&
      (sm_qos_r99aim_to_bitrate(aim_qos_r99->guar_br_ul) < min_qos_r99->guar_br_ul))
  {
    (void)TRACE_EVENT_P2("R99 GUARANTEED UPLINK BITRATE parameter "
                         "insufficient: %d < %d (min_qos)",
                         sm_qos_r99aim_to_bitrate(aim_qos_r99->guar_br_ul),
                         min_qos_r99->guar_br_ul);
    return TRUE;
  }

  /* Guaranteed downlink bit-rate  - Note: Only for real-time traffic class traffic! */
  if ((aim_qos_r99->tc == (U8)M_SM_QOS_TC_CONV
       || aim_qos_r99->tc == (U8)M_SM_QOS_TC_STREAM) &&
      (min_qos_r99->guar_br_dl != (U16)PS_GUAR_BR_DL_SUB) &&
      (sm_qos_r99aim_to_bitrate(aim_qos_r99->guar_br_dl) < min_qos_r99->guar_br_dl))
  {
    (void)TRACE_EVENT_P2("R99 GUARANTEED DOWNLINK BITRATE parameter "
                         "insufficient: %d < %d (min_qos)",
                         sm_qos_r99aim_to_bitrate(aim_qos_r99->guar_br_dl),
                         min_qos_r99->guar_br_dl);
    return TRUE;
  }

  return FALSE;
}

/*
+------------------------------------------------------------------------------
| Function    : sm_qos_is_minimum_satisfied_by_aim
+------------------------------------------------------------------------------
| Description : Main comparison function. Compares a 3G air interface message
|               QoS parameter set (R97 or R99) with the minimum QoS parameter
|               set in context data.
|               Returns TRUE if the 3G air message parameters are greater than
|               or equal to the minimum parameters in SM representation
|               ("greater than" is different for each value).
|
| Parameters  : context          - Context data
|               aim_qos          - air interface QoS structure
+------------------------------------------------------------------------------
*/
BOOL sm_qos_is_minimum_satisfied_by_aim(struct T_SM_CONTEXT_DATA *context,
                                        T_M_SM_qos               *aim_qos)
{
  BOOL  less_than = TRUE;

#ifdef DEBUG_VERBOSE
  (void)TRACE_FUNCTION( "sm_qos_is_minimum_satisfied_by_aim");
#endif
   /*lint -e613 (Possible use of null pointer 'context' in left argument to operator '->') */
  TRACE_ASSERT(context != NULL);

  if (context->minimum_qos.ctrl_qos == PS_is_qos_not_present) {
      /* Accept QoS without any checking */
    return TRUE; 
  } 
  if (context->minimum_qos.ctrl_qos == PS_is_R97) {
    T_PS_qos_r97 *min_qos = &context->minimum_qos.qos.qos_r97;
    T_PS_qos_r97 *req_qos = &context->requested_qos.qos.qos_r97;
    if (aim_qos->v_qos_r99 == (U8)FALSE) {
      less_than = sm_qos_compare_r97aim_lt_r97(&aim_qos->qos_r97, min_qos, 
                                               req_qos);
    } else { 

     /* This #if.. #else part is required because we currently do not set
      * RAT when MMPM_ATTACH_IND is received. When the RAT is set properly 
      * retain ONLY the #else part. 
      */
/*#ifdef SM_EDGE */ /*ONLY FOR GPRS WORLD*/
      /*In GPRS world try comparing only R97 part of QoS*/
      TRACE_EVENT("GSM/GPRS RAT.. Comparing only R97 part of QoS.");
      less_than = sm_qos_compare_r97aim_lt_r97(&aim_qos->qos_r97, min_qos, 
                                               req_qos);
#if 0
      /*#else*/ /*FOR UMTS and DUAL-MODE*/
      if (sm_get_current_rat() == PS_RAT_GSM){
        /*In GPRS world compare only R97 part of QoS*/
        TRACE_EVENT("GSM/GPRS RAT.. Comparing only R97 part of QoS.");
        less_than = sm_qos_compare_r97aim_lt_r97(&aim_qos->qos_r97, min_qos,
                                                 req_qos);
      } else { /*We should be in UMTS world now..*/
        T_PS_qos_r99      tmp_qos;
        TRACE_EVENT("Calling cl_qos_convert_r97_to_r99() for min qos");
        cl_qos_convert_r97_to_r99(&context->minimum_qos.qos.qos_r97, &tmp_qos);
        TRACE_EVENT("UMTS RAT.. Comparing only R99 part of QoS.");
        less_than = sm_qos_compare_r99aim_lt_r99(&aim_qos->qos_r99, &tmp_qos);
      }
      /*#endif*/
#endif

    }
  }
  else if (context->minimum_qos.ctrl_qos == PS_is_R99) {
    T_PS_qos_r99        tmp_qos;
    if (aim_qos->v_qos_r99 == (U8)FALSE) {
      cl_qos_convert_r97_to_r99(&context->minimum_qos.qos.qos_r97, &tmp_qos);
    }
  else {
      memcpy(&tmp_qos, &context->minimum_qos.qos.qos_r99, sizeof(T_PS_qos_r99));
    }

    less_than = sm_qos_compare_r99aim_lt_r99(&aim_qos->qos_r99, &tmp_qos);
  } else { /* if (context->ctrl_minimum_qos) */
    (void)TRACE_EVENT_P1("ERROR! Invalid union controller == %d in minimum QoS!",
                         context->minimum_qos.ctrl_qos);
  }

  return !less_than;
}

/*
+------------------------------------------------------------------------------
| Function    : sm_qos_is_minimum_satisfied_by_aim
+------------------------------------------------------------------------------
| Description : Comparison function. Compares a the minimum QoS parameter set
|               configured in context data with another QoS structure in
|               internal SM representation.  Returns TRUE if the comparison QoS
|               parameters are greater than or equal to the minimum parameters.
|               ("greater than" is different for each value).
|
| Parameters  : context          - Context data
|               sm_qos           - comparison QoS structure
+------------------------------------------------------------------------------
*/
BOOL sm_qos_is_minimum_satisfied_by_sm(struct T_SM_CONTEXT_DATA *context,
                                       T_SM_qos                 *sm_qos)
{
  BOOL  less_than = TRUE;

#ifdef DEBUG_VERBOSE
  (void)TRACE_FUNCTION( "sm_qos_is_minimum_satisfied_by_sm");
#endif
   /*lint -e613 (Possible use of null pointer 'context' in left argument to operator '->') */
  TRACE_ASSERT(context != NULL);

  if (context->minimum_qos.ctrl_qos == PS_is_qos_not_present) {
    /* Accept QoS without any checking */
    return TRUE; 
  } 
  if (context->minimum_qos.ctrl_qos == PS_is_R97) {
    T_PS_qos_r97 *min_qos = &context->minimum_qos.qos.qos_r97;
    if (sm_qos->ctrl_qos == PS_is_R97) {
      /*
        less_than = sm_qos_compare_r97_lt_r97(&sm_qos->qos.qos_r97, min_qos);
        * Construct not used, as this function is called only during
        * MO modify procedures, which is a R99 feature.
      */
      less_than = FALSE;
    } else {
      T_PS_qos_r99      tmp_qos;
      cl_qos_convert_r97_to_r99(min_qos, &tmp_qos);

      less_than = sm_qos_compare_r99_lt_r99(&sm_qos->qos.qos_r99, &tmp_qos);
    }
  } else if (context->minimum_qos.ctrl_qos == PS_is_R99) {
    T_PS_qos_r99 *min_qos = &context->minimum_qos.qos.qos_r99;
    if (sm_qos->ctrl_qos == PS_is_R97) {
      T_PS_qos_r99      tmp_qos;
      cl_qos_convert_r97_to_r99(&sm_qos->qos.qos_r97, &tmp_qos);

      less_than = sm_qos_compare_r99_lt_r99(&tmp_qos, min_qos);
    } else {
      less_than = sm_qos_compare_r99_lt_r99(&sm_qos->qos.qos_r99, min_qos);
    }
  } else {
    (void)TRACE_EVENT_P1("ERROR! Invalid union controller == %d in minimum QoS!",
                       context->minimum_qos.ctrl_qos);
    }


  return !less_than;
}

/*
+------------------------------------------------------------------------------
| Function    : sm_qos_copy_from_sm
+------------------------------------------------------------------------------
| Description : Copy QoS structure and union controller from context data to
|               another QoS structure (in a primitive etc).
|
| Parameters  : dst              - Destination QoS structure
|               src              - Source QoS structure
|               dctrl            - Destination union controller
+------------------------------------------------------------------------------
*/
void sm_qos_copy_from_sm(T_PS_qos *dst, T_SM_qos *src, T_PS_ctrl_qos *dctrl)
{
  *dctrl     = src->ctrl_qos;
  memcpy(dst, &src->qos, sizeof(T_PS_qos));
}

/*
+------------------------------------------------------------------------------
| Function    : sm_qos_copy_to_sm
+------------------------------------------------------------------------------
| Description : Copy QoS structure and union controller from e.g. a primitive
|               to context data.
|
| Parameters  : dst              - Destination QoS structure (in context data)
|               src              - Source QoS structure
|               ctrl             - Source union controller
+------------------------------------------------------------------------------
*/
void sm_qos_copy_to_sm(T_SM_qos *dst, T_PS_qos *src, T_PS_ctrl_qos ctrl)
{
  dst->ctrl_qos  = ctrl;
  memcpy(&dst->qos, src, sizeof(T_PS_qos));
}

/*
+------------------------------------------------------------------------------
| Function    : sm_qos_is_requested_qos_present
+------------------------------------------------------------------------------
| Description : Returns TRUE if requested QoS element is present.
|
| Parameters  : context          - Context data
+------------------------------------------------------------------------------
*/
BOOL sm_qos_is_requested_qos_present(struct T_SM_CONTEXT_DATA *context)
{
   /*lint -e613 (Possible use of null pointer 'context' in left argument to operator '->') */
  TRACE_ASSERT(context != NULL);

  return (context->requested_qos.ctrl_qos != PS_is_qos_not_present);
}

/*
+------------------------------------------------------------------------------
| Function    : sm_rank_del_contexts_based_on_tc
+------------------------------------------------------------------------------
| Description : Ranks all contexts linked to a pdp address based on traffic 
|               class and traffic handling priority. Locally deactivates all 
|               but the best ranking context. Informs ACI about the deactivation.
|               Also sends a pdp context status request to GMM.
|               Reference 23.107 Annex C
|
| Parameters  : None
+------------------------------------------------------------------------------
*/
#ifdef TI_PS_OP_SM_RETAIN_BEST_CONTEXT

U16 sm_rank_del_contexts_based_on_tc()
{
  U8  nsapi        = 0;
  U8  temp_ti      = 0; 
  U8  count        = 0; 
  U8  temp_nsapi   = 0;
  U16 context_ti   = 0;
  U16 linked_contexts  = 0;
  U8  best_qos_rank = SM_HIGH_VALUE; /*Initialize with a high value*/
  U16 nsapis_to_deactivate = 0;

  for (nsapi=(int)NAS_NSAPI_5; nsapi < NAS_SIZE_NSAPI; nsapi++)
  {
     struct T_SM_CONTEXT_DATA *context;
     struct T_SM_CONTEXT_DATA *temp_context;
  
     context = sm_get_context_data_from_nsapi(nsapi);
     /* The context_control_state is checked to see if the context is already 
      * getting deactivated
      */
     if ( context != NULL && 
        (context->context_control_state != SM_CONTEXT_DEACTIVATED) )
     {
       if (sm_is_secondary(context))
       {
         context_ti = context->linked_ti;
       } else {
         context_ti = context->ti;
       }
       linked_contexts = sm_add_nsapi_to_nsapi_set(nsapi, linked_contexts);
       count = 1; /*One context is already active*/ 
       /*Rank this context. May be needed if linked contexts are active*/
       context->qos_rank = sm_qos_rank_context(context->ti);
       if (context->qos_rank < best_qos_rank) {
           best_qos_rank = context->qos_rank;
       }
     } else { /*Go to the next nsapi*/
       continue; 
     }
     /* One context active. Check all the remaining active contexts */
     for (temp_nsapi=nsapi; temp_nsapi < NAS_SIZE_NSAPI; temp_nsapi++)
     {
       temp_context = sm_get_context_data_from_nsapi(temp_nsapi); 
       if(temp_context != NULL && temp_context->context_control_state
                                  !=  SM_CONTEXT_DEACTIVATED)
       {
          if (sm_is_secondary(temp_context))
          {
            temp_ti = temp_context->linked_ti;
          } else {
            temp_ti = temp_context->ti;
          }
          if ( (temp_nsapi != nsapi) && (temp_ti == context_ti) )
          {
            linked_contexts = sm_add_nsapi_to_nsapi_set(temp_nsapi, 
                                                   linked_contexts);
            count++; 
            /*A second context active. Rank it. Pass the ti of the context*/
            temp_context->qos_rank = sm_qos_rank_context(temp_context->ti);
            if (temp_context->qos_rank < best_qos_rank) {
              best_qos_rank = temp_context->qos_rank;
            }
          }
        }
     }

     if (count >=2 )
     {
       for (temp_nsapi=(int)NAS_NSAPI_5; temp_nsapi<NAS_SIZE_NSAPI; temp_nsapi++)
       {
         struct T_SM_CONTEXT_DATA *context;
         if( (linked_contexts & (0x0001 << temp_nsapi)) != 0 )
         {
           context = sm_get_context_data_from_nsapi(temp_nsapi); 
           if ( (context->qos_rank > best_qos_rank) && (context != NULL)
                && (context->context_control_state != SM_CONTEXT_DEACTIVATED) )
           { 
              (void)TRACE_EVENT_P1("Deactivating nsapi >>TC based << %d; ", temp_nsapi);
              /*deactivate the context*/
              nsapis_to_deactivate = sm_add_nsapi_to_nsapi_set(temp_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);
            } 
          }
       }
     }
  }

return nsapis_to_deactivate;
}
#endif /*#ifdef TI_PS_OP_SM_RETAIN_BEST_CONTEXT*/


/*
+------------------------------------------------------------------------------
| Function    : sm_qos_rank_context
+------------------------------------------------------------------------------
| Description : Provides a rank for the context based on the tc and handling pri
|               Reference 23.107 Annex C
|
| Parameters  : context ti
+------------------------------------------------------------------------------
*/
#ifdef TI_PS_OP_SM_RETAIN_BEST_CONTEXT

U8 sm_qos_rank_context(U16 ti)
{
  struct T_SM_CONTEXT_DATA *context;
  T_PS_qos_r99 temp_qos;
  U8           qos_rank = 0;

  context = sm_get_context_data_from_ti(ti);

  if(context->accepted_qos.ctrl_qos == PS_is_R97)
  { /*R97 QoS. Convert to R99 QoS before ranking*/
     (void)cl_qos_convert_r97_to_r99(&context->accepted_qos.qos.qos_r97,
                                     &temp_qos);
  } else
  {
     temp_qos = context->accepted_qos.qos.qos_r99;
  }

  switch(temp_qos.tc)
  {
    case PS_TC_INTER:
      switch(temp_qos.handling_pri)
      {
        case PS_HANDLING_PRI_1: 
          qos_rank = 1; 
          break;
        case PS_HANDLING_PRI_2: 
          qos_rank = 4; 
          break;
        case PS_HANDLING_PRI_3: 
          qos_rank = 5; 
          break;
       }
      break;
    case PS_TC_CONV:
      qos_rank = 2; 
      break;
    case PS_TC_STREAM:
      qos_rank = 3; 
      break;
    case PS_TC_BG:
      qos_rank = 6; 
      break;
    default:
      qos_rank = 6;
      break;
  }
  return qos_rank;
}
#endif /*#ifdef TI_PS_OP_SM_RETAIN_BEST_CONTEXT*/

/*
+------------------------------------------------------------------------------
| Function    : sm_retain_cntxt_wth_best_bitrate
+------------------------------------------------------------------------------
| Description : Deactivates all the linked pdp contexts except the one with 
|               highest max bit rate uplink or downlink. Informs ACI about the 
|               deactivation.
|               Also sends a pdp context status request to GMM.
|               Reference 23.107 Annex C
|
| Parameters  : None
+------------------------------------------------------------------------------
*/
#ifdef TI_PS_OP_SM_RETAIN_BEST_CONTEXT

U16 sm_retain_cntxt_wth_best_bitrate()
{
  U8  nsapi      = 0;
  U8  temp_nsapi = 0;
  U8  count      = 0;
  U16 linked_contexts      = 0;        
  U16 best_max_bitrate     = 0;
  U16 nsapis_to_deactivate = 0;
  U16 context_ti           = 0;
  U16 temp_ti              = 0;
  T_PS_qos_r99 temp_qos;


  for (nsapi = (int)NAS_NSAPI_5; nsapi < NAS_SIZE_NSAPI; nsapi++)
  {
    struct T_SM_CONTEXT_DATA *context;
    struct T_SM_CONTEXT_DATA *temp_context;

    context = sm_get_context_data_from_nsapi(nsapi);
    if ( (context != NULL) && (context->context_control_state 
                               != SM_CONTEXT_DEACTIVATED) )
    {
      if (sm_is_secondary(context))
      {
        context_ti = context->linked_ti;
      } else {
        context_ti = context->ti;
      }
      linked_contexts = sm_add_nsapi_to_nsapi_set(nsapi, linked_contexts);
      count = 1; /*One context is already active*/ 
    } else { /* Process the next nsapi */
      continue; 
    }

    for(temp_nsapi=nsapi; temp_nsapi < NAS_SIZE_NSAPI; temp_nsapi++)
    {
      temp_context = sm_get_context_data_from_nsapi(temp_nsapi); 
      if(temp_context != NULL && temp_context->context_control_state
                               !=  SM_CONTEXT_DEACTIVATED)
      {
         if (sm_is_secondary(temp_context))
         {
           temp_ti = temp_context->linked_ti;
         } else {
           temp_ti = temp_context->ti;
         }
         if ( (temp_nsapi != nsapi) && (temp_ti == context_ti) )
         {
           linked_contexts = sm_add_nsapi_to_nsapi_set(temp_nsapi, 
                                                       linked_contexts);
           count++; 
         }
      }
   }

   if (count >=2)
   { /*Multiple contexts active for this ti. Find the best_max_bit rate.*/
     (void)TRACE_EVENT( "Multiple contexts active: sm_retain_cntxt_wth_best_bitrate" );
     for (temp_nsapi=(int)NAS_NSAPI_5; temp_nsapi < NAS_SIZE_NSAPI; temp_nsapi++)
     {
       if( (linked_contexts & (0x0001 << temp_nsapi)) != 0 )
       {
          context = sm_get_context_data_from_nsapi(temp_nsapi); 
          if(context->accepted_qos.ctrl_qos == PS_is_R97)
          { /*R97 QoS. Convert to R99 QoS before ranking*/
            (void)cl_qos_convert_r97_to_r99(&context->accepted_qos.qos.qos_r97,
                                            &temp_qos);
          } else  {
            temp_qos = context->accepted_qos.qos.qos_r99;
          }
          if (temp_qos.max_rate_dl >= best_max_bitrate)
          {
             best_max_bitrate = temp_qos.max_rate_dl; 
          } else if (temp_qos.max_rate_ul >= best_max_bitrate)  {
              best_max_bitrate = temp_qos.max_rate_ul; 
          } 
        }
     }

   }

   if (count >=2) 
   {
     for (temp_nsapi=(int)NAS_NSAPI_5; temp_nsapi < NAS_SIZE_NSAPI; temp_nsapi++)
     {
       if( (linked_contexts & (0x0001 << temp_nsapi)) != 0 )
       {
          context = sm_get_context_data_from_nsapi(temp_nsapi); 
          if(context->accepted_qos.ctrl_qos == PS_is_R97)
          { /*R97 QoS. Convert to R99 QoS before ranking*/
            (void)cl_qos_convert_r97_to_r99(&context->accepted_qos.qos.qos_r97,
                                            &temp_qos);
          } else  {
            temp_qos = context->accepted_qos.qos.qos_r99;
          }
          if ( (temp_qos.max_rate_dl == best_max_bitrate) || 
               (temp_qos.max_rate_ul == best_max_bitrate) )
          { 
             /* Don't do anything */
          } 
          else /* Deactivate the context */
          { 
             (void)TRACE_EVENT_P1("Deactivating nsapi << bit rate based >> %d; ", temp_nsapi);
             /*Deactivate the context*/
             nsapis_to_deactivate = sm_add_nsapi_to_nsapi_set(temp_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);
          }
       }
     }
   }

  }
 return nsapis_to_deactivate;
}

#endif /*#ifdef TI_PS_OP_SM_RETAIN_BEST_CONTEXT*/

/*
+------------------------------------------------------------------------------
| Function    : sm_retain_cntxt_with_least_nsapi
+------------------------------------------------------------------------------
| Description : Deactivates all the linked pdp contexts except the one with 
|               least nsapi value. Informs ACI about the deactivation.
|               Also sends a pdp context status request to GMM.
|               Reference 23.107 Annex C
|
| Parameters  : None
+------------------------------------------------------------------------------
*/
#ifdef TI_PS_OP_SM_RETAIN_BEST_CONTEXT

U16 sm_retain_cntxt_with_least_nsapi()
{
  struct T_SM_CONTEXT_DATA *context;
  struct T_SM_CONTEXT_DATA *temp_context;
  U8  nsapi                = 0;
  U8  temp_nsapi           = 0;
  U16 linked_contexts      = 0;
  U16 nsapis_to_deactivate = 0;
  U16 best_max_bitrate     = 0;
  U8  min_nsapi = SM_HIGH_VALUE; /*Initialize to a high value. Here 255*/
  U8  count                = 0;
  U16 context_ti,temp_ti   = 0;


  for (nsapi = (int)NAS_NSAPI_5; nsapi < NAS_SIZE_NSAPI; nsapi++)
  {
    context = sm_get_context_data_from_nsapi(nsapi);
    if ( (context != NULL) && (context->context_control_state != 
                           SM_CONTEXT_DEACTIVATED) )
    {
      if (sm_is_secondary(context))
       {
         context_ti = context->linked_ti;
       } else {
         context_ti = context->ti;
       }
       linked_contexts = sm_add_nsapi_to_nsapi_set(nsapi, linked_contexts);
       count = 1; /*One context is already active*/ 
     } else { /*Go to the next nsapi*/
       continue;
     }
    
    /* Check all the remaining active contexts */
     for (temp_nsapi=nsapi; temp_nsapi < NAS_SIZE_NSAPI; temp_nsapi++)
     {
       temp_context = sm_get_context_data_from_nsapi(temp_nsapi);
       if(temp_context != NULL && temp_context->context_control_state
                                  !=  SM_CONTEXT_DEACTIVATED)
       {
          if (sm_is_secondary(temp_context))
          {
            temp_ti = temp_context->linked_ti;
          } else {
            temp_ti = temp_context->ti;
          }
          if ( (temp_nsapi != nsapi) && (temp_ti == context_ti) )
          {
            linked_contexts = sm_add_nsapi_to_nsapi_set(temp_nsapi, 
                                                        linked_contexts);
            count++; 
          }
        }
     }


    if (count >= 2)
    { /*Multiple contexts active for this ti. Try deactivating.*/
      (void)TRACE_EVENT( "Multiple contexts active: sm_retain_cntxt_with_least_nsapi" );
      for (temp_nsapi=(int)NAS_NSAPI_5; temp_nsapi<NAS_SIZE_NSAPI; temp_nsapi++)
      {
        if( (linked_contexts & (0x0001 << temp_nsapi)) != 0 )
        {
          temp_context = sm_get_context_data_from_nsapi(temp_nsapi);
          if ((min_nsapi == SM_HIGH_VALUE) && (temp_context != NULL) && 
              (temp_context->context_control_state != SM_CONTEXT_DEACTIVATED) )
          {
            min_nsapi = temp_nsapi;
          } else
            {
               (void)TRACE_EVENT_P1("Deactivating nsapi >>nsapi based << %d; ", temp_nsapi);
               /*Deactivate the context*/
               nsapis_to_deactivate = sm_add_nsapi_to_nsapi_set(temp_nsapi, 
                                                      nsapis_to_deactivate);
               /* Make sure to clear the pending reactivation flag */
               sm_set_context_pending_reactivation(temp_context, FALSE);
               /* Order context deactivation */
               sm_context_control(temp_context, SM_I_CONTEXT_LOCAL_DEACTIVATE,
                                                                (void *)TRUE);
            }
         }
      }
    }
 }
  return nsapis_to_deactivate;
}

#endif /*#ifdef TI_PS_OP_SM_RETAIN_BEST_CONTEXT*/