FreeCalypso > hg > fc-tourmaline
view src/g23m-gprs/sm/sm_qos.c @ 263:b5e8dfd114a7
Switch_ON(): go into charging boot mode on either CHGSTS or CHGPRES
If someone were to plug and then unplug a charger into a switched-off
phone in an extremely brief "glitch" manner, we should do an automatic
power-off on boot in this condition. When we were checking only
CHGPRES in Switch_ON(), we would go into Misc boot state instead,
which is undesirable. Now if we have a CHGSTS but not CHGPRES
condition, we will go into charging boot mode, and FCBM will then
do the automatic power-off upon detecting absence of the charger
in its periodic polling.
author | Mychaela Falconia <falcon@freecalypso.org> |
---|---|
date | Fri, 14 May 2021 05:50:36 +0000 |
parents | fa8dc04885d8 |
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*/