view src/g23m-gprs/cl/clqos.c @ 303:f76436d19a7a default tip

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

/*-----------------------------------------------------------------------------
|  Project :  3G UMTS PS
|  Module  :  CL
+------------------------------------------------------------------------------
|             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:    QoS handling functions.
|             For design details, see:
|             8010.149 CL SAP
+----------------------------------------------------------------------------*/

#ifndef CLQOS_C
#define CLQOS_C
#endif

#ifndef ENTITY_CL
#define ENTITY_CL
#endif
#define ENTITY_CLT

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

#include "typedefs.h"   /* to get TI data types */
#include "vsi.h"        /* to get a lot of macros */
#include "gsm.h"        /* to get a lot of macros */
#include "message.h"    /* to get the message definitions for sm */
#include "prim.h"       /* to get the definitions of used SAP and directions */
#include "p_cl.val"  /* to get the type of sgsn_rel*/
#include "p_8010_152_ps_include.h" /*to get T_PS_qos_rXX*/
#include "cl_inline.h"


typedef U8 T_CL_result; /*I really don't like this ! Can we do something in cl.sap ?*/

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

/*
 * Conversion table for 1/n in T_ratio.
 * Contains 1/n * 1E9 entries used for scaling.
 */
static const U32 cl_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 ====================================================*/

/*-----------------------------------------------------------------------------
| Function    : cl_qos_ratio_to_U32
+------------------------------------------------------------------------------
| Description : Conversion function: T_ratio to integer
|
| Parameters  : T_ratio
|
| Return      : ratio as integer
+----------------------------------------------------------------------------*/
static U32 cl_qos_ratio_to_U32(U8 ratio_mant, U8 ratio_exp)
{
  U32   value;
  int   count;

  /* Error checks */
  if (ratio_exp > (U8)9) 
  {
    /*TRACE_ERROR( "QoS ratio exponent parameter out of bounds!" );*/
    return 0;
  }
  else
  {
    if (ratio_mant > (U8)9)
    {
      /*TRACE_ERROR( "QoS ratio exponent parameter out of bounds!" );*/
      return 0;
    }
    else
    {
      if (ratio_exp == (U8)9 && ratio_mant < (U8)5)
      {
        /*TRACE_ERROR( "QoS ratio parameter exceeds data type range!" );*/
        return 0;
      }
    }
  }

  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 = (cl_qos_ratio_table[(U16)ratio_mant] + (value >> 1)) / value;

  return value;
}

/*-----------------------------------------------------------------------------
| Function    : cl_qos_r99_to_delay
+------------------------------------------------------------------------------
| Description : Given an input R99 QoS, return the corresponding R97 delay
|               parameter
|
| Parameters  : R99 Qos
|
| Return      : Delay
+----------------------------------------------------------------------------*/
static T_PS_delay cl_qos_r99_to_delay(T_PS_qos_r99 *src_qos_r99)
{
  T_PS_delay delay;

  /* Traffic class / handling priority */
  switch ((T_PS_tc)src_qos_r99->tc)
  {
    case PS_TC_SUB:       /* 0 - Subscribed */
      delay               = PS_DELAY_SUB;
      break;
    case PS_TC_CONV:      /* 1 - Conversational */
      delay               = PS_DELAY_1;
      break;
    case PS_TC_STREAM:    /* 2 - Streaming */
      delay               = PS_DELAY_1;
      break;
    case PS_TC_INTER:     /* 3 - Interactive */
      switch (src_qos_r99->handling_pri) 
      {
        case PS_HANDLING_PRI_1:
          delay             = PS_DELAY_1;
          break;
        case PS_HANDLING_PRI_2:
          delay             = PS_DELAY_2;
          break;
        case PS_HANDLING_PRI_3:
        default:            /* For safety */
          delay             = PS_DELAY_3;
          break;
      }   /* switch */
      break;
    case PS_TC_BG:        /* 4 - Background */
      delay               = PS_DELAY_4;
      break;
    default:              /* 5, 6, 7 or other garbage */
      delay               = PS_DELAY_SUB;
      break;
  }     /* switch */

  return delay;
} /* cl_qos_r99_to_delay */

/*-----------------------------------------------------------------------------
| Function    : cl_qos_r99_to_relclass
+------------------------------------------------------------------------------
| Description : Given an input R99 QoS, return the corresponding R97 relclass
|               parameter
|
| Parameters  : R99 Qos
|
| Return      : Reliability class
+----------------------------------------------------------------------------*/
static T_PS_relclass cl_qos_r99_to_relclass(T_PS_qos_r99 *src_qos_r99)
{
  U32             sdu_err;
  T_PS_relclass  relclass;

  /* First, convert SDU error ratio to integral number */
  sdu_err = cl_qos_ratio_to_U32(src_qos_r99->sdu_err_ratio.ratio_mant,
                                src_qos_r99->sdu_err_ratio.ratio_exp);

  if (sdu_err == 0UL)
  {             /* SDU err ratio == subscribed value */
    relclass       = PS_RELCLASS_SUB;      /* 0 */
  }
  else
  {
    if (sdu_err >= 100000UL)
    { /* SDU err ratio <= 1E-5 */
      relclass         = PS_LLC_RLC_PROT;    /* 2 */
    }
    else
    {
      if (sdu_err >= 2000UL)
      {  /* 1E-5 < SDU err ratio <= 5E-4 */
        relclass         = PS_RLC_PROT;        /* 3 */
      }
      else
      {                        /* SDU err ratio > 5E-4 */
        if (src_qos_r99->ber.ratio_exp > (U8)4 ||
            (src_qos_r99->ber.ratio_exp == (U8)4 && src_qos_r99->ber.ratio_mant >= (U8)2))
        {
                                  /* BER <= 2E-4 */
          relclass       = PS_PROT;              /* 4 */
        }
        else
        {                    /* BER > 2E-4 */
          relclass       = PS_NO_REL;            /* 5 */
        }
      }
    }
  }  /* if (sdu_err ...) */

  return relclass;
}

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

/*-----------------------------------------------------------------------------
| Function    : cl_qos_convert_r99_to_r97_req
+------------------------------------------------------------------------------
| Description : Function for converting a R99 QoS parameter set to a R97/R98
|               ditto. Conversion rules according to [3G 23.107, sec 9]
|
| Parameters  : Originating R99 QoS parameter struct
|               Destination R97 QoS parameter struct (overwritten in this function)
|
| Return      : Conversion result and Destination R97 QoS parameter struct.
+----------------------------------------------------------------------------*/
T_CL_result cl_qos_convert_r99_to_r97(T_PS_qos_r99 *src_qos_r99,
                                      T_PS_qos_r97 *dst_qos_r97)
{
  T_CL_result  success = (T_CL_result)TRUE;
  U16          count, max_rate;

  dst_qos_r97->delay    = (U8)cl_qos_r99_to_delay(src_qos_r99);

  /*
   * SDU error ratio / Residual bit error ratio
   */
  dst_qos_r97->relclass = (U8)cl_qos_r99_to_relclass(src_qos_r99);

  /*
   * Maximum bit-rate
   *
   * In case max uplink and downlink data rates differ, [3G 23.107]
   * says to map according to max(R99_uplink, R99_downlink),
   * i.e. the higher of the two.
   */
  max_rate = (src_qos_r99->max_rate_ul > src_qos_r99->max_rate_dl ?
              src_qos_r99->max_rate_ul : src_qos_r99->max_rate_dl);

  if (max_rate == (U16)PS_MAX_BR_UL_SUB)
  { /* Subscribed */
    dst_qos_r97->peak = (U8)PS_PEAK_SUB;
  }
  else
  { /* ! Subscribed */
    /* Perform pseudo log2(bit-rate).  Really, we are finding the highest set
     * bit to use in our comparison rather than making a *huge*
     * if-[else if]*-else clause.
     * Fixed ceiling to 2048 (count < 12) => max_rate <= 2^11 */
    for (count = (U16)4; ((max_rate >> count) != 0) && count < (U16)12; count++);

    /* The quick / code optimal way:
     * dst_qos_r97->peak = count - 3; */

    switch (count)
    {
      case 4:  dst_qos_r97->peak = (U8)PS_PEAK_1K;   break;
      case 5:  dst_qos_r97->peak = (U8)PS_PEAK_2K;   break;
      case 6:  dst_qos_r97->peak = (U8)PS_PEAK_4K;   break;
      case 7:  dst_qos_r97->peak = (U8)PS_PEAK_8K;   break;
      case 8:  dst_qos_r97->peak = (U8)PS_PEAK_16K;  break;
      case 9:  dst_qos_r97->peak = (U8)PS_PEAK_32K;  break;
      case 10: dst_qos_r97->peak = (U8)PS_PEAK_64K;  break;
      case 11: dst_qos_r97->peak = (U8)PS_PEAK_128K; break;
      case 12: dst_qos_r97->peak = (U8)PS_PEAK_256K; break;
    }   /* switch */
  }     /* if - Subscribed */

  /* R97 peak throughput class is fixed to 31 (best effort) */
  dst_qos_r97->mean   = (U8)PS_MEAN_BEST;
  dst_qos_r97->preced = (U8)PS_PRECED_SUB;

  return success;
}

/*-----------------------------------------------------------------------------
| Function    : cl_qos_convert_r97_to_r99_req
+------------------------------------------------------------------------------
| Description : Function for converting a R97/R98 QoS parameter set to a R99
|               ditto. Conversion rules according to [3G 23.107, sec 9]
|
| Parameters  : Originating R97 QoS parameter struct
|               Destination R99 QoS parameter struct (overwritten in this function)
|
| Return      : Conversion result and Destination R99 QoS parameter struct.
+----------------------------------------------------------------------------*/
T_CL_result cl_qos_convert_r97_to_r99(T_PS_qos_r97 *src_qos_r97,
                                      T_PS_qos_r99 *dst_qos_r99)
{
  T_CL_result  success = (T_CL_result)TRUE;

  /* Delay parameter */
  switch ((T_PS_delay)src_qos_r97->delay)
  {
    case PS_DELAY_SUB:               /* 0 */
      dst_qos_r99->tc           = (U8)PS_TC_SUB;
      dst_qos_r99->handling_pri = (U8)PS_HANDLING_PRI_SUB;
      break;
    case PS_DELAY_1:                 /* 1 */
      dst_qos_r99->tc           = (U8)PS_TC_INTER;
      dst_qos_r99->handling_pri = (U8)PS_HANDLING_PRI_1;
      break;
    case PS_DELAY_2:                 /* 2 */
      dst_qos_r99->tc           = (U8)PS_TC_INTER;
      dst_qos_r99->handling_pri = (U8)PS_HANDLING_PRI_2;
      break;
    case PS_DELAY_3:                 /* 3 */
      dst_qos_r99->tc           = (U8)PS_TC_INTER;
      dst_qos_r99->handling_pri = (U8)PS_HANDLING_PRI_3;
      break;
    case PS_DELAY_4:                 /* 4 */
      dst_qos_r99->tc           = (U8)PS_TC_BG;
      dst_qos_r99->handling_pri = (U8)PS_HANDLING_PRI_SUB;
      break;
    default:                      /* 5 - 7 or other garbage */
      /* Error handling? */
      success = (T_CL_result)FALSE;
      break;
  }     /* switch */

  /* Reliability class */
  switch ((T_PS_relclass)src_qos_r97->relclass)
  {
    case PS_RELCLASS_SUB:                      /* 0 - Subscribed */
      dst_qos_r99->sdu_err_ratio.ratio_mant = dst_qos_r99->sdu_err_ratio.ratio_exp = (U8)0;
      dst_qos_r99->ber.ratio_mant = dst_qos_r99->ber.ratio_exp = (U8)0;
      dst_qos_r99->del_err_sdu = (U8)PS_DEL_ERR_SUB;
      break;
    case PS_GTP_LLC_RLC_PROT:                  /* 1 - Acknowledged GTP, LLC, and RLC */
      dst_qos_r99->sdu_err_ratio.ratio_mant = (U8)1;
      dst_qos_r99->sdu_err_ratio.ratio_exp  = (U8)6; /* SDU error ratio = 1E-6 */
      dst_qos_r99->ber.ratio_mant           = (U8)1;
      dst_qos_r99->ber.ratio_exp            = (U8)5; /* Residual BER = 1E-5 */
      dst_qos_r99->del_err_sdu = (U8)PS_DEL_ERR_NO;
      break;
    case PS_LLC_RLC_PROT:                      /* 2 - Unacknowledged GTP; Acknowledged LLC and RLC */
      dst_qos_r99->sdu_err_ratio.ratio_mant = (U8)1;
      dst_qos_r99->sdu_err_ratio.ratio_exp  = (U8)6; /* SDU error ratio = 1E-6 */
      dst_qos_r99->ber.ratio_mant           = (U8)1;
      dst_qos_r99->ber.ratio_exp            = (U8)5; /* Residual BER = 1E-5 */
      dst_qos_r99->del_err_sdu = (U8)PS_DEL_ERR_NO;
      break;
    case PS_RLC_PROT:                          /* 3 - Unacknowledged GTP and LLC; Acknowledged RLC */
      dst_qos_r99->sdu_err_ratio.ratio_mant = (U8)1;
      dst_qos_r99->sdu_err_ratio.ratio_exp  = (U8)4; /* SDU error ratio = 1E-4 */
      dst_qos_r99->ber.ratio_mant           = (U8)1;
      dst_qos_r99->ber.ratio_exp            = (U8)5; /* Residual BER = 1E-5 */
      dst_qos_r99->del_err_sdu = (U8)PS_DEL_ERR_NO;
      break;
    case PS_PROT:                              /* 4 - Unacknowledged GTP, LLC, and RLC */
      dst_qos_r99->sdu_err_ratio.ratio_mant = (U8)1;
      dst_qos_r99->sdu_err_ratio.ratio_exp  = (U8)3; /* SDU error ratio = 1E-3 */
      dst_qos_r99->ber.ratio_mant           = (U8)1;
      dst_qos_r99->ber.ratio_exp            = (U8)5; /* Residual BER = 1E-5 */
      dst_qos_r99->del_err_sdu = (U8)PS_DEL_ERR_NO;
      break;
    case PS_NO_REL:                            /* 5 - Unacknowledged GTP, LLC, and RLC (Unprotected) */
      dst_qos_r99->sdu_err_ratio.ratio_mant = (U8)1;
      dst_qos_r99->sdu_err_ratio.ratio_exp  = (U8)3; /* SDU error ratio = 1E-3 */
      dst_qos_r99->ber.ratio_mant           = (U8)4;
      dst_qos_r99->ber.ratio_exp            = (U8)3; /* Residual BER = 4E-3 */
      dst_qos_r99->del_err_sdu = (U8)PS_DEL_ERR_YES;
      break;
    default:                                      /* 6, 7 or other garbage */
      /* Error handling? */
      success = (T_CL_result)FALSE;
      break;
  }     /* switch */

  /* Peak throughput parameter */
  /* NOTE: Could be optimized heavily into the following:
   * dst_qos_r99->max_br_dl = dst_qos_r99->max_br_ul = (peak == 0 ? 0 : 4 << peak)
   */

  switch ((T_PS_peak)src_qos_r97->peak)
  {
    case PS_PEAK_SUB:             /* 0 */
      dst_qos_r99->max_rate_dl = dst_qos_r99->max_rate_ul = (U16)PS_MAX_BR_UL_SUB;
      break;
    case PS_PEAK_1K:              /* 1 */
      dst_qos_r99->max_rate_dl = dst_qos_r99->max_rate_ul = (U16)8;
      break;
    case PS_PEAK_2K:              /* 2 */
      dst_qos_r99->max_rate_dl = dst_qos_r99->max_rate_ul = (U16)16;
      break;
    case PS_PEAK_4K:              /* 3 */
      dst_qos_r99->max_rate_dl = dst_qos_r99->max_rate_ul = (U16)32;
      break;
    case PS_PEAK_8K:              /* 4 */
      dst_qos_r99->max_rate_dl = dst_qos_r99->max_rate_ul = (U16)64;
      break;
    case PS_PEAK_16K:             /* 5 */
      dst_qos_r99->max_rate_dl = dst_qos_r99->max_rate_ul = (U16)128;
      break;
    case PS_PEAK_32K:             /* 6 */
      dst_qos_r99->max_rate_dl = dst_qos_r99->max_rate_ul = (U16)256;
      break;
    case PS_PEAK_64K:             /* 7 */
      dst_qos_r99->max_rate_dl = dst_qos_r99->max_rate_ul = (U16)512;
      break;
    case PS_PEAK_128K:            /* 8 */
      dst_qos_r99->max_rate_dl = dst_qos_r99->max_rate_ul = (U16)1024;
      break;
    case PS_PEAK_256K:            /* 9 */
      dst_qos_r99->max_rate_dl = dst_qos_r99->max_rate_ul = (U16)2048;
      break;
    default:
      /* Handling? */
      success = (T_CL_result)FALSE;
      break;
  }     /* switch */

  /* R99 max SDU size is fixed to 1500 */
  dst_qos_r99->max_sdu  = (U16)1500;

  /* Remaining R99 fields are not specified. We fill them in with subscribed */
  dst_qos_r99->order      =  (U8)PS_ORDER_SUB;
  dst_qos_r99->xfer_delay = (U16)PS_XFER_DELAY_SUB;
  dst_qos_r99->guar_br_ul = (U16)PS_GUAR_BR_UL_SUB;
  dst_qos_r99->guar_br_dl = (U16)PS_GUAR_BR_DL_SUB;

  return success;
}

/*-----------------------------------------------------------------------------
| Function    : cl_qos_use_ack_mode_llc_req
+------------------------------------------------------------------------------
| Description : Query function: Given input QoS, should NSAPI use acknowledged
|               LLC mode?
|
| Parameters  : Originating R99 QoS parameter struct
|
| Return      : LLC mode
+----------------------------------------------------------------------------*/
T_PS_llc_mode cl_qos_use_ack_mode_llc(T_PS_qos_r99 *src_qos_r99)
{
  T_PS_relclass  relclass;
  T_PS_llc_mode   ret_val = PS_LLC_UNACKNOWLEDGED;

  relclass = cl_qos_r99_to_relclass(src_qos_r99);

  if (relclass == PS_GTP_LLC_RLC_PROT || relclass == PS_LLC_RLC_PROT)
  {
    ret_val = PS_LLC_ACKNOWLEDGED;
  }

  return ret_val;
}