FreeCalypso > hg > fc-tourmaline
diff src/g23m-gprs/llc/llc_uf.c @ 1:fa8dc04885d8
src/g23m-*: import from Magnetite
author | Mychaela Falconia <falcon@freecalypso.org> |
---|---|
date | Fri, 16 Oct 2020 06:25:50 +0000 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/g23m-gprs/llc/llc_uf.c Fri Oct 16 06:25:50 2020 +0000 @@ -0,0 +1,4152 @@ + /* ++----------------------------------------------------------------------------- +| Project : +| Modul : ++----------------------------------------------------------------------------- +| Copyright 2002 Texas Instruments Berlin, AG +| All rights reserved. +| +| This file is confidential and a trade secret of Texas +| Instruments Berlin, AG +| 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 Berlin, AG. ++----------------------------------------------------------------------------- +| Purpose : This modul is part of the entity LLC and implements all +| procedures and functions as described in the +| SDL-documentation (U-statemachine) ++----------------------------------------------------------------------------- +*/ + + +#ifndef LLC_UF_C +#define LLC_UF_C +#endif + +#define ENTITY_LLC + +/*==== INCLUDES =============================================================*/ + +#include <string.h> /* to get memcpy() */ + +#include "typedefs.h" /* to get Condat data types */ +#include "vsi.h" /* to get a lot of macros */ +#include "macdef.h" +#include "gprs.h" +#include "gsm.h" /* to get a lot of macros */ +#include "cnf_llc.h" /* to get cnf-definitions */ +#include "mon_llc.h" /* to get mon-definitions */ +#include "prim.h" /* to get the definitions of used SAP and directions */ +#include "llc.h" /* to get the global entity definitions */ + +#include "llc_f.h" /* to get global functions, e.g. llc_init_parameters */ +#include "llc_uf.h" /* to get local XID definitions */ +#include "llc_t200s.h" /* to get signal interface to T200 */ +#include "llc_txs.h" /* to get signal interface to TX */ + +/*==== CONST ================================================================*/ + +/*==== LOCAL VARS ===========================================================*/ + +/*==== PRIVATE FUNCTIONS ====================================================*/ + +/*==== PUBLIC FUNCTIONS =====================================================*/ + + +/* + * CHECK_INSTANCE: + * v(ariable) - variable name of parameter + */ +#define CHECK_INSTANCE(v) \ + if (llc_data->decoded_xid.##v##.valid) \ + { \ + /* \ + * More than one instance of the same XID parameter type is included. \ + */ \ + if (cr_bit EQ SGSN_COMMAND) \ + { \ + /* \ + * XID command: Ignore all instances except the first. \ + * <R.LLC.XIDNEG_R.A.014> \ + */ \ + continue; \ + } \ + else /* SGSN_RESPONSE */ \ + { \ + /* \ + * XID response: The XID information field is regarded as invalid. \ + * <R.LLC.XID_INVA.A.012> \ + */ \ + TRACE_0_INFO ("More than one instance of an XID-IE in RSP"); \ + return FALSE; \ + } \ + } + + +/* + * CHECK_LENGTH_VALUE: + * l(ength) - default length of parameter + * c(ondition) - out-of-range value condition + * v(ariable) - variable name of parameter + * d(default) - default value of parameter + */ +#define CHECK_LENGTH_VALUE(l,c,v,d) \ + if ((length NEQ l) OR (c)) \ + { \ + /* \ + * Unsupported length or out-of-range values. \ + */ \ + if (cr_bit EQ SGSN_COMMAND) \ + { \ + /* \ + * XID command: Set the value for this type according to our \ + * preferences. \ + * <R.LLC.XIDNEG_R.A.003> \ + */ \ + llc_data->decoded_xid.##v##.valid = TRUE; \ + llc_data->decoded_xid.##v##.value = d; \ + break; \ + } \ + else /* SGSN_RESPONSE */ \ + { \ + /* \ + * XID response: The XID information field is regarded as invalid. \ + * <R.LLC.XID_INVA.A.007>, <R.LLC.XID_INVA.A.009> \ + */ \ + TRACE_0_INFO ("Unsupported length or out-of-range values in RSP"); \ + return FALSE; \ + } \ + } + + +/* + * CHECK_SENSE_OF_NEGOTIATION: + * d(ata) - received data value to check + * v(ariable) - variable name of parameter + * s(ense) - sense of negotiation (XID_SENSE_UP / XID_SENSE_DOWN) + */ +#define CHECK_SENSE_OF_NEGOTIATION(d,v,s) \ + { \ + /* \ + * XID response must not contain an XID parameter with a value that \ + * violates the sense of negotiation. \ + * <R.LLC.XID_INVA.A.008> \ + */ \ + if (cr_bit EQ SGSN_RESPONSE) \ + { \ + /* \ + * First compare the value with the requested value, if there is any \ + */ \ + if ( (llc_data->u->requested_xid.##v##.valid) AND \ + (llc_data->u->requested_xid.##v##.value s d) ) \ + { \ + TRACE_0_INFO ("Sense of negotiation error"); \ + return FALSE; \ + } \ + else if( !(llc_data->u->requested_xid.##v##.valid) AND \ + (*(llc_data->##v) s d) ) \ + { \ + /* \ + * If the value was not requested but included in response, compare \ + * it with the current value \ + */ \ + TRACE_0_INFO ("Value not requested, but sense of negotiation error"); \ + return FALSE; \ + } \ + } \ + } + + +/* ++------------------------------------------------------------------------------ +| Function : u_init ++------------------------------------------------------------------------------ +| Description : This procedure initialises all necessary variables of u_frames +| for all SAPIs. +| +| Parameters : +| ++------------------------------------------------------------------------------ +*/ +GLOBAL void u_init (void) +{ + TRACE_FUNCTION( "u_init" ); + + /* + * Initialise every incarnation of U with state TLLI_UNASSIGNED. + */ + SWITCH_SERVICE (llc, u, 0); + INIT_STATE (U_0, U_TLLI_UNASSIGNED); + + SWITCH_SERVICE (llc, u, 1); + INIT_STATE (U_1, U_TLLI_UNASSIGNED); + + SWITCH_SERVICE (llc, u, 2); + INIT_STATE (U_2, U_TLLI_UNASSIGNED); + + SWITCH_SERVICE (llc, u, 3); + INIT_STATE (U_3, U_TLLI_UNASSIGNED); + + SWITCH_SERVICE (llc, u, 4); + INIT_STATE (U_4, U_TLLI_UNASSIGNED); + + SWITCH_SERVICE (llc, u, 5); + INIT_STATE (U_5, U_TLLI_UNASSIGNED); + + return; +} /* u_init() */ + + + +/* ++------------------------------------------------------------------------------ +| Function : u_init_sapi ++------------------------------------------------------------------------------ +| Description : This procedure initialises all necessary variables of u_frames +| for the given SAPI after a TLLI assignment of LLC was occurred. +| +| Parameters : +| ++------------------------------------------------------------------------------ +*/ +GLOBAL void u_init_sapi (void) +{ + TRACE_FUNCTION( "u_init_sapi" ); + + /* + * Initial, no XID command is currently being sent. + */ + llc_data->u->xid_pending = FALSE; + + llc_data->u->xid_tag = 0L; + llc_data->u->xid_tag_negotiate = 0L; + llc_data->u->xid_tag_sent = 0L; + + llc_data->u->release_requested = FALSE; + llc_data->u->ll_xid_resp_pending = FALSE; + + return; +} /* u_init_sapi() */ + +/* ++------------------------------------------------------------------------------ +| Function : u_build_u_frame ++------------------------------------------------------------------------------ +| Description : This procedure fills the given sdu with a correct LLC frame, +| containing the address field and the control field (U format). +| The information field is not filled in by this procedure. +| The checksum field (FCS) is also not filled in by this +| procedure, it is filled in by the send_pdu service before +| sending. +| +| Parameters : sdu - a valid pointer to an SDU, containing enough octets for +| the requested command/response +| cr_bit - C/R bit for the frame +| sapi - a valid SAPI for unacknowledged operation +| pf_bit - P/F bit for the U frame +| command - a valid command/response for the U frame +| ++------------------------------------------------------------------------------ +*/ +GLOBAL void u_build_u_frame +( +#ifdef LL_DESC + T_desc_list3 *desc_list3, +#else + T_sdu *sdu, +#endif + T_BIT cr_bit, + UBYTE sapi, + T_BIT pf_bit, + T_COMMAND command +) +{ +#ifdef LL_DESC + T_desc3 *desc3; + UBYTE *desc_buf; +#endif + + TRACE_FUNCTION( "u_build_u_frame" ); + +#ifdef LL_DESC + /* + * Let every frame begin at the beginning. + */ + desc3 = (T_desc3*)desc_list3->first; + desc3->offset = 0; + desc_buf = (UBYTE*)desc3->buffer; + + /* + * Fill in address and control fields. + * NOTE: The values of the commands are chosen to correspond with the + * defined values of the Mx bits in the control field. Therefore the given + * command can be used to build the header, regardless of its actual value. + */ + desc_buf[0] = ((UBYTE)cr_bit << 6) | sapi; + desc_buf[1] = 0xE0 | ((UBYTE)pf_bit << 4) | (UBYTE)command; + + + /* + * Determine the length of the frame, depending on the command/ + * response. + */ + switch (command) + { +#ifdef REL99 + case U_NULL: + /* + * The NULL command contains no information field. The size + * of the frame is fixed. (FCS ist not yet considered in l_buf, will + * be included when FCS is appended to the frame.) + */ + desc3->len = U_NULL_SIZE_BITS/8; + break; +#endif /* REL99 */ + case U_DM: + /* + * The DM response contains no information field. The size + * of the frame is fixed. (FCS ist not yet considered in l_buf, will + * be included when FCS is appended to the frame.) + */ + desc3->len = U_DM_SIZE_BITS/8; + break; + case U_DISC: + /* + * The DISC response contains no information field. The size + * of the frame is fixed. (FCS ist not yet considered in l_buf, will + * be included when FCS is appended to the frame.) + */ + desc3->len = U_DISC_SIZE_BITS/8; + break; + case U_UA: + /* + * The UA response may contain an information field. Set only the + * size of the header (address + control fields). + */ + desc3->len = 2; + break; + case U_SABM: + /* + * The SABM command may contain an information field. Set only the + * size of the header (address + control fields). + */ + desc3->len = 2; + break; + case U_FRMR: + /* + * The FRMR response contains an information field of fixed size. + * u_insert_frmr_information() relies on a sane value in l_buf, + * therefore set only the size of the header (address + control field). + */ + desc3->len = 2; + break; + case U_XID: + /* + * The XID command/response contains an information field. Set only the + * size of the header (address + control fields). + */ + desc3->len = 2; + break; + default: + desc3->len = 0; + TRACE_ERROR ("Undefined Command specified"); + break; + } + + desc_list3->list_len = desc3->len; + + return; + +#else + /* + * Let every frame begin at the beginning. + */ + sdu->o_buf = 0; + + /* + * Fill in address and control fields. + * NOTE: The values of the commands are chosen to correspond with the + * defined values of the Mx bits in the control field. Therefore the given + * command can be used to build the header, regardless of its actual value. + */ + sdu->buf[0] = ((UBYTE)cr_bit << 6) | sapi; + sdu->buf[1] = 0xE0 | ((UBYTE)pf_bit << 4) | (UBYTE)command; + + + /* + * Determine the length of the frame, depending on the command/ + * response. + */ + switch (command) + { +#ifdef REL99 + case U_NULL: + /* + * The NULL command contains no information field. The size + * of the frame is fixed. (FCS ist not yet considered in l_buf, will + * be included when FCS is appended to the frame.) + */ + sdu->l_buf = U_NULL_SIZE_BITS; + break; +#endif /* REL99 */ + + case U_DM: + /* + * The DM response contains no information field. The size + * of the frame is fixed. (FCS ist not yet considered in l_buf, will + * be included when FCS is appended to the frame.) + */ + sdu->l_buf = U_DM_SIZE_BITS; + break; + case U_DISC: + /* + * The DISC response contains no information field. The size + * of the frame is fixed. (FCS ist not yet considered in l_buf, will + * be included when FCS is appended to the frame.) + */ + sdu->l_buf = U_DISC_SIZE_BITS; + break; + case U_UA: + /* + * The UA response may contain an information field. Set only the + * size of the header (address + control fields). + */ + sdu->l_buf = 2*8; + break; + case U_SABM: + /* + * The SABM command may contain an information field. Set only the + * size of the header (address + control fields). + */ + sdu->l_buf = 2*8; + break; + case U_FRMR: + /* + * The FRMR response contains an information field of fixed size. + * u_insert_frmr_information() relies on a sane value in l_buf, + * therefore set only the size of the header (address + control field). + */ + sdu->l_buf = 2*8; + break; + case U_XID: + /* + * The XID command/response contains an information field. Set only the + * size of the header (address + control fields). + */ + sdu->l_buf = 2*8; + break; + default: + sdu->l_buf = 0; + TRACE_ERROR ("Undefined Command specified"); + break; + } + + return; +#endif /* LL_DESC */ +} /* u_build_u_frame() */ + + + +/* ++------------------------------------------------------------------------------ +| Function : u_check_xid ++------------------------------------------------------------------------------ +| Description : This procedure checks the XID information field that is given +| in SDU. If it is invalid, rc_xid_valid is set to FALSE, +| otherwise to TRUE. All XID parameters are decoded and stored +| in llc_decoded_xid (global variable) if they are valid. The +| parameters cr_bit and command are necessary to distinguish +| between the different commands and responses. Each parameter +| is checked for multiple instances, correct sense of +| negotiation, and valid field length and value. +| +| Parameters : sdu - a valid pointer to an SDU +| cr_bit - C/R bit of the frame +| command - command/response of the U frame +| ++------------------------------------------------------------------------------ +*/ +GLOBAL BOOL u_check_xid (T_sdu *sdu, + T_BIT cr_bit, + T_COMMAND command) +{ + UBYTE *par; + BOOL xl; + UBYTE type; + UBYTE length = 0; + UBYTE *data; + ULONG value; + + + TRACE_FUNCTION( "u_check_xid" ); + + /* + * Initialise each valid flag to FALSE. + */ + llc_data->decoded_xid.version.valid = FALSE; + llc_data->decoded_xid.iov_ui.valid = FALSE; + llc_data->decoded_xid.iov_i.valid = FALSE; + llc_data->decoded_xid.t200.valid = FALSE; + llc_data->decoded_xid.n200.valid = FALSE; + llc_data->decoded_xid.n201_u.valid = FALSE; + llc_data->decoded_xid.n201_i.valid = FALSE; + llc_data->decoded_xid.md.valid = FALSE; + llc_data->decoded_xid.mu.valid = FALSE; + llc_data->decoded_xid.kd.valid = FALSE; + llc_data->decoded_xid.ku.valid = FALSE; + llc_data->decoded_xid.reset.valid = FALSE; + + llc_data->decoded_l3_xid.valid = FALSE; + + /* + * Initialise par with the first XID parameter. + * Process each XID parameter until the end of the list is reached: + * - Check if end of list is reached (max. SDU length). + * ...process XID parameter (set xl and length)... + * - Increment par to point to the next XID parameter (is incremented by + * (data) length plus header length). + */ +#ifdef TRACE_EVE + { + int i; + int l = sdu->l_buf/8; + int o = sdu->o_buf/8; + + vsi_o_ttrace(VSI_CALLER TC_EVENT, "XID sdu dump len:%d bytes", l); + + for (i=0; i<l; i+=10) + { + vsi_o_ttrace(VSI_CALLER TC_EVENT, "%.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x", + sdu->buf[o+i+0], sdu->buf[o+i+1], + sdu->buf[o+i+2], sdu->buf[o+i+3], sdu->buf[o+i+4], + + sdu->buf[o+i+5], sdu->buf[o+i+6], + sdu->buf[o+i+7], sdu->buf[o+i+8], sdu->buf[o+i+9]); + } + } +#endif + + for (par = &(sdu->buf[sdu->o_buf/8]); + par < (&(sdu->buf[sdu->o_buf/8]) + sdu->l_buf/8); + par += length + (xl ? 2 : 1)) + { + /* + * Store XL bit in boolean xl. + */ + xl = (*par & 0x80) > 0; + + /* + * Store (data) length, and set data to point to the beginning + * of the XID parameter data. + */ + if (xl) + { + /* + * XL == 1: length is given in 8 bits. + */ + length = (*par & 0x03) << 6; + length += (*(par+1) & 0xFC) >> 2; + + data = par + 2; + } + else + { + /* + * XL == 0: length is given in 2 bits. + */ + length = *par & 0x03; + + data = par + 1; + } + + + if (data + length > &(sdu->buf[sdu->o_buf/8]) + sdu->l_buf/8) + { + /* + * The XID parameter field violates the LLC frame format (exceeds given + * SDU size). + * <R.LLC.XID_INVA.A.001> + */ + return FALSE; + } + + + /* + * Store type field. + */ + type = (*par & 0x7C) >> 2; + + + /* + * Check type and store parameter data, if any. + */ + switch (type) + { + case XID_VERSION: + CHECK_INSTANCE (version); + + /* + * XID response must not contain an XID parameter with a value that + * violates the sense of negotiation. + * <R.LLC.XID_INVA.A.008> + */ + if (cr_bit EQ SGSN_RESPONSE) + { + /* + * First compare the value with the requested value, if there is any + */ + if ( (llc_data->u->requested_xid.version.valid) AND + (llc_data->u->requested_xid.version.value XID_SENSE_DOWN (*data & 0x0F)) ) + { + return FALSE; + } + else if( !(llc_data->u->requested_xid.version.valid) AND + llc_data->version XID_SENSE_DOWN (*data & 0x0F) ) + { + /* + * If the value was not requested but included in response, compare + * it with the current value + */ + return FALSE; + } + } + /*lint -e685 Relational operator always evaluates to false*/ + CHECK_LENGTH_VALUE (XID_VERSION_LEN, + ((*data & 0x0F) > XID_VERSION_MAX), + version, llc_data->version); + /*lint +e685 Relational operator always evaluates to false*/ + /* + * Set version as valid and store value. + */ + llc_data->decoded_xid.version.valid = TRUE; + llc_data->decoded_xid.version.value = *data & 0x0F; + TRACE_1_INFO("XID Version:%d", llc_data->decoded_xid.version.value); + break; + case XID_IOV_UI: + CHECK_INSTANCE (iov_ui); + + value = ((ULONG)*data) << 24; + value += ((ULONG)*(data+1)) << 16; + value += ((ULONG)*(data+2)) << 8; + value += *(data+3); + /*lint -e685 Relational operator always evaluates to false*/ + CHECK_LENGTH_VALUE (XID_IOV_UI_LEN, + (value > XID_IOV_UI_MAX), + iov_ui, llc_data->iov_ui); + /*lint +e685 Relational operator always evaluates to false*/ + /* + * Set iov_ui as valid and store value. + */ + llc_data->decoded_xid.iov_ui.valid = TRUE; + llc_data->decoded_xid.iov_ui.value = value; + TRACE_1_INFO("XID IOV-UI:0x%.8X", value); + break; + case XID_IOV_I: + if (command EQ U_XID) + { + /* + * IOV-I must not be contained in an XID command/response. + * <R.LLC.XID_INVA.A.010>, <R.LLC.XID_PAR.A.003> + */ + TRACE_0_INFO("IOV-I must not be contained in an XID command/response"); + return FALSE; + } + + CHECK_INSTANCE (iov_i); + + value = ((ULONG)*data) << 24; + value += ((ULONG)*(data+1)) << 16; + value += ((ULONG)*(data+2)) << 8; + value += *(data+3); + /*lint -e685 Relational operator always evaluates to false*/ + CHECK_LENGTH_VALUE (XID_IOV_I_LEN, + (value > XID_IOV_I_MAX), + iov_i, *(llc_data->iov_i)); + /*lint +e685 Relational operator always evaluates to false*/ + /* + * Set iov_i as valid and store value. + */ + llc_data->decoded_xid.iov_i.valid = TRUE; + llc_data->decoded_xid.iov_i.value = value; + TRACE_1_INFO("XID IOV-I:0x%.8X", value); + break; + case XID_T200: + CHECK_INSTANCE (t200); + + value = ((ULONG)*data) << 8; + value += *(data+1); + value &= 0x00000FFFL; + + /* + * XID response must not contain an XID parameter with a value that + * violates the sense of negotiation. + * <R.LLC.XID_INVA.A.008> + */ + if (cr_bit EQ SGSN_RESPONSE) + { + /* + * First compare the value with the requested value, if there is any + */ + if ( (llc_data->u->requested_xid.t200.valid) AND + (llc_data->u->requested_xid.t200.value XID_SENSE_UP value) ) + { + TRACE_1_INFO("Value of T200:%d illegal", value); + return FALSE; + } + else if( !(llc_data->u->requested_xid.t200.valid) AND + (USHORT)INT2XID(llc_data->t200->length) XID_SENSE_UP value ) + { + /* + * If the value was not requested but included in response, compare + * it with the current value + */ + TRACE_1_INFO("Requested value of T200:%d illegal", value); + return FALSE; + } + } + + CHECK_LENGTH_VALUE (XID_T200_LEN, + ((value < XID_T200_MIN) OR (value > XID_T200_MAX)), + t200, (USHORT)INT2XID(llc_data->t200->length)); + + /* + * Set t200 as valid and store value. + */ + llc_data->decoded_xid.t200.valid = TRUE; + llc_data->decoded_xid.t200.value = (USHORT)value; + TRACE_1_INFO("XID T200:%d", llc_data->decoded_xid.t200.value); + break; + case XID_N200: + CHECK_INSTANCE (n200); + + CHECK_SENSE_OF_NEGOTIATION ((*data & 0x0F), n200, XID_SENSE_UP); + /*lint -e685 Relational operator always evaluates to false*/ + CHECK_LENGTH_VALUE (XID_N200_LEN, + (((*data & 0x0F) < XID_N200_MIN) OR ((*data & 0x0F) > XID_N200_MAX)), + n200, *(llc_data->n200)); + /*lint +e685 Relational operator always evaluates to false*/ + /* + * Set n200 as valid and store value. + */ + llc_data->decoded_xid.n200.valid = TRUE; + llc_data->decoded_xid.n200.value = *data & 0x0F; + TRACE_1_INFO("XID N200:%d", llc_data->decoded_xid.n200.value); + break; + case XID_N201_U: + CHECK_INSTANCE (n201_u); + + value = ((ULONG)*data) << 8; + value += *(data+1); + value &= 0x000007FFL; + + CHECK_SENSE_OF_NEGOTIATION ((USHORT)value, n201_u, XID_SENSE_DOWN); + + CHECK_LENGTH_VALUE (XID_N201_U_LEN, + (((USHORT)value < XID_N201_U_MIN) OR (value > XID_N201_U_MAX)), + n201_u, *(llc_data->n201_u)); + + /* + * Set n201_u as valid and store value. + */ + llc_data->decoded_xid.n201_u.valid = TRUE; + llc_data->decoded_xid.n201_u.value = (USHORT)value; + TRACE_1_INFO("XID N201-U:%d", llc_data->decoded_xid.n201_u.value); + break; + case XID_N201_I: + CHECK_INSTANCE (n201_i); + + value = ((ULONG)*data) << 8; + value += *(data+1); + value &= 0x000007FFL; + + CHECK_SENSE_OF_NEGOTIATION ((USHORT)value, n201_i, XID_SENSE_DOWN); + + CHECK_LENGTH_VALUE (XID_N201_I_LEN, + ((value < XID_N201_I_MIN) OR (value > XID_N201_I_MAX)), + n201_i, *(llc_data->n201_i)); + + /* + * Set n201_i as valid and store value. + */ + llc_data->decoded_xid.n201_i.valid = TRUE; + llc_data->decoded_xid.n201_i.value = (USHORT)value; + TRACE_1_INFO("XID N201-I:%d", llc_data->decoded_xid.n201_i.value); + break; + case XID_MD: + CHECK_INSTANCE (md); + + value = ((ULONG)*data) << 8; + value += *(data+1); + value &= 0x00007FFFL; + + CHECK_SENSE_OF_NEGOTIATION ((USHORT)value, md, XID_SENSE_DOWN); + + CHECK_LENGTH_VALUE (XID_MD_LEN, + ((value != XID_MD_OFF) AND ((value < XID_MD_MIN) OR (value > XID_MD_MAX))), + md, *(llc_data->md)); + + /* + * Set md as valid and store value. + */ + llc_data->decoded_xid.md.valid = TRUE; + llc_data->decoded_xid.md.value = (USHORT)value; + TRACE_1_INFO("XID MD:%d", llc_data->decoded_xid.md.value); + break; + case XID_MU: + CHECK_INSTANCE (mu); + + value = ((ULONG)*data) << 8; + value += *(data+1); + value &= 0x00007FFFL; + + CHECK_SENSE_OF_NEGOTIATION ((USHORT)value, mu, XID_SENSE_DOWN); + + CHECK_LENGTH_VALUE (XID_MU_LEN, + ((value != XID_MU_OFF) AND ((value < XID_MU_MIN) OR (value > XID_MU_MAX))), + mu, *(llc_data->mu)); + + /* + * Set mu as valid and store value. + */ + llc_data->decoded_xid.mu.valid = TRUE; + llc_data->decoded_xid.mu.value = (USHORT)value; + TRACE_1_INFO("XID MU:%d", llc_data->decoded_xid.mu.value); + break; + case XID_KD: + CHECK_INSTANCE (kd); + + CHECK_SENSE_OF_NEGOTIATION (*data, kd, XID_SENSE_DOWN); + /*lint -e685 Relational operator always evaluates to false*/ + CHECK_LENGTH_VALUE (XID_KD_LEN, + ((*data < XID_KD_MIN) OR (*data > XID_KD_MAX)), + kd, *(llc_data->kd)); + /*lint +e685 Relational operator always evaluates to false*/ + /* + * Set kd as valid and store value. + */ + llc_data->decoded_xid.kd.valid = TRUE; + llc_data->decoded_xid.kd.value = *data; + TRACE_1_INFO("XID KD:%d", llc_data->decoded_xid.kd.value); + break; + case XID_KU: + CHECK_INSTANCE (ku); + + CHECK_SENSE_OF_NEGOTIATION (*data, ku, XID_SENSE_DOWN); + /*lint -e685 Relational operator always evaluates to false*/ + CHECK_LENGTH_VALUE (XID_KU_LEN, + ((*data < XID_KU_MIN) OR (*data > XID_KU_MAX)), + ku, *(llc_data->ku)); + /*lint +e685 Relational operator always evaluates to false*/ + /* + * Set ku as valid and store value. + */ + llc_data->decoded_xid.ku.valid = TRUE; + llc_data->decoded_xid.ku.value = *data; + TRACE_1_INFO("XID KU:%d", llc_data->decoded_xid.ku.value); + break; + case XID_LAYER_3: + if (llc_data->decoded_l3_xid.valid) + { + /* + * More than one instance of the same XID parameter type is included. + */ + if (cr_bit EQ SGSN_COMMAND) + { + /* + * XID command: Ignore all instances except the first. + * <R.LLC.XIDNEG_R.A.014> + */ + continue; + } + else /* SGSN_RESPONSE */ + { + /* + * XID response: The XID information field is regarded as invalid. + * <R.LLC.XID_INVA.A.012> + */ + TRACE_0_INFO("More than one L3 parameters are included in XID response."); + return FALSE; + } + } + + if ((llc_data->current_sapi NEQ LL_SAPI_3) AND + (llc_data->current_sapi NEQ LL_SAPI_5) AND + (llc_data->current_sapi NEQ LL_SAPI_9) AND + (llc_data->current_sapi NEQ LL_SAPI_11)) + { + /* + * Layer-3 parameters on a SAPI different from 3, 5, 9, and 11. + * <R.LLC.XID_INVA.A.011> + */ + TRACE_0_INFO("L3 parameters on a SAPI different from 3, 5, 9, and 11."); + return FALSE; + } + + /* + * Set layer_3 as valid and store Layer-3 XID parameters. + */ + llc_data->decoded_l3_xid.valid = TRUE; + llc_data->decoded_l3_xid.length = length; + memcpy (llc_data->decoded_l3_xid.value, data, length); + TRACE_1_INFO("XID L3:%d bytes", length); + break; + case XID_RESET: + if (par NEQ &(sdu->buf[sdu->o_buf/8])) + { + /* + * Reset must be the first parameter in the XID information field. + * <R.LLC.XID_INVA.A.004> + */ + TRACE_0_INFO("Reset must be the first XID parameter."); + return FALSE; + } + + if (cr_bit EQ SGSN_COMMAND) + { + if (command EQ U_SABM) + { + /* + * SABM command: Reset is not allowed. + * <R.LLC.XID_INVA.A.003> + */ + TRACE_0_INFO("Reset is not allowed in SABM command"); + return FALSE; + } + } + else /* SGSN_RESPONSE */ + { + /* + * UA/XID response: Reset is not allowed. + * <R.LLC.XID_INVA.A.005> + */ + TRACE_0_INFO("Reset is not allowed in UA/XID response"); + return FALSE; + } + + /* + * Set reset as valid. + */ + llc_data->decoded_xid.reset.valid = TRUE; + TRACE_0_INFO("XID Reset"); + break; + default: + /* + * Unrecognised type field. + */ + if (cr_bit EQ SGSN_COMMAND) + { + /* + * XID command: Skip XID parameter. + * <R.LLC.XIDNEG_R.A.002> + */ + continue; + } + else /* SGSN_RESPONSE */ + { + /* + * XID response: The XID information field is regarded as invalid. + * <R.LLC.XID_INVA.A.006> + */ + TRACE_0_INFO("Unrecognised type field in XID response."); + return FALSE; + } + } + } + + /* + * All XID parameters have been processed, XID information field seems + * to be correct. + */ + return TRUE; +} /* u_check_xid() */ + + + +/* ++------------------------------------------------------------------------------ +| Function : u_eval_xid ++------------------------------------------------------------------------------ +| Description : This procedure evaluates the decoded XID information field in +| llc_decoded_xid (global variable). If Reset is present, the +| state of every incarnation in ABM is set to ADM (including +| the current incarnation!), all LLC layer parameters are set +| to their default values, the OCs for unack. transfer are set +| to 0, and the parameter reset_received is set to TRUE. If a +| command has been received (cr_bit is set to SGSN_COMMAND), +| all XID parameters are evaluated and stored as global values, +| if acceptable. Each XID parameter is tagged in llc_xid_tag +| for the next response. Otherwise (a response has been +| received), store the received parameters as global values +| and reset their valid flags in llc_requested_xid. In each +| case, if N201-I or N201-U have been changed, or if Layer-3 +| XID parameters have been received, set xid_ind to TRUE, +| otherwise to FALSE. Additionally remove any received parameter +| in llc_xid_negotiate, because it has been explicitly +| negotiated by the peer. +| +| Parameters : cr_bit - SGSN_COMMAND/_RESPONSE has been received +| reset_received - TRUE indicates received Reset parameter, +| else FALSE +| xid_ind - TRUE indicates changed N201-U and/or N201-I, +| so that LL_XID_IND must be sent to layer 3 +| ++------------------------------------------------------------------------------ +*/ +GLOBAL void u_eval_xid (T_BIT cr_bit, + BOOL *reset_received, + BOOL *xid_ind) +{ + UBYTE incarnation; + + + TRACE_FUNCTION ("u_eval_xid"); + + /* + * Preset both indicators with default values. + */ + *reset_received = FALSE; + *xid_ind = FALSE; + + /* + * Preset global XID indicator for next response. + */ + llc_data->u->xid_tag = 0L; + + + /************************************************************************** + * Reset: + * Has to be processed first in order to take effect! + */ + if (llc_data->decoded_xid.reset.valid) + { + /* + * Initialise all LLC layer parameters for all SAPIs. + */ + llc_init_parameters(); + llc_init_requested_xid(); + + /* + * Reset all incarnations of service U that are in state 'ABM' to 'ADM'. + */ + for (incarnation = 0; incarnation < U_NUM_INC; incarnation++) + { + llc_data->u_base[incarnation].state = U_ADM; + } + + /* + * LLC has to be reset. Send SIG_U_LLME_RESET_IND. + */ + *reset_received = TRUE; + } + + + /************************************************************************** + * Version: + * Shall not be negotiated while in state 'ABM'. + * <R.LLC.XID_PAR.A.001> + */ + if ((llc_data->decoded_xid.version.valid) AND (GET_STATE(U) NEQ U_ABM)) + { + if (cr_bit EQ SGSN_COMMAND) + { + if (llc_xid_value_acceptable (llc_data->current_sapi, XID_VERSION, + llc_data->decoded_xid.version.value)) + { + llc_data->version = llc_data->decoded_xid.version.value; + } + else /* value is not acceptable */ + { + /* + * Sense of negotiation down: offer lower value; sense of + * negotiation up: offer higher value. + * This happens automatically by just responding with our current + * global value. + */ + } + + /* + * Tag parameter for next response. + */ + llc_data->u->xid_tag |= 0x00000001L << XID_VERSION; + } + else /* SGSN_RESPONSE */ + { + /* + * Sense of negotiation-correctness of response has already been checked + * in u_check_xid(). + */ + llc_data->version = llc_data->decoded_xid.version.value; + } + + /* + * Remove tag in xid_tag_negotiate, because this parameter has been + * explicitly negotiated by the peer. + * <R.LLC.XIDNEG_R.A.015> + */ + llc_data->u->xid_tag_negotiate &= ~(0x00000001L << XID_VERSION); + } + + + /************************************************************************** + * IOV-UI: + * Shall only be negotiated in state 'ADM'. <R.LLC.XID_PAR.A.002> + */ + if ((llc_data->decoded_xid.iov_ui.valid) AND (GET_STATE(U) NEQ U_ABM) ) + { + /* + * Do not tag iov_ui for the XID response, because it shall only be + * transmitted in downlink direction. + * <R.LLC.XID_PAR.A.004> + */ + + /* + * Copy value to global variable (no sense of negotiation). + */ + llc_data->iov_ui = llc_data->decoded_xid.iov_ui.value; + } + + + /************************************************************************** + * IOV-I: + * Shall only be negotiated with SABM and UA frames. This is already + * checked in u_check_xid(). + * <R.LLC.XID_PAR.A.003>, <R.LLC.XID_INVA.A.010> + */ + if (llc_data->decoded_xid.iov_i.valid) + { + /* + * Do not tag iov_i for the XID response, because it shall only be + * transmitted in downlink direction. + * <R.LLC.XID_PAR.A.004> + */ + + /* + * Copy value to global variable (no sense of negotiation). + */ + *(llc_data->iov_i) = llc_data->decoded_xid.iov_i.value; + } + + + /************************************************************************** + * T200 (and T201): + * Can be negotiated in state 'ADM' and 'ABM'. New values shall only apply + * to timers set after the negotiation has been completed (which is + * accomplished automatically). + * <R.LLC.XID_PAR.A.006>, <R.LLC.XID_PAR.A.007> + */ + if (llc_data->decoded_xid.t200.valid) + { + if (cr_bit EQ SGSN_COMMAND) + { + if (llc_xid_value_acceptable (llc_data->current_sapi, XID_T200, + llc_data->decoded_xid.t200.value)) + { + llc_data->t200->length = XID2INT (llc_data->decoded_xid.t200.value); + } + else /* value is not acceptable */ + { + /* + * Sense of negotiation down: offer lower value; sense of + * negotiation up: offer higher value. + * This happens automatically by just responding with our current + * global value. + */ + } + + /* + * In case we also want to negotiate this value, but to a lower! + * value, delete this parameter from the requested XID structure + */ + if (llc_data->u->requested_xid.t200.valid AND + llc_data->u->requested_xid.t200.value <= llc_data->decoded_xid.t200.value) + { + llc_data->u->requested_xid.t200.valid = FALSE; + } + + /* + * Tag parameter for next response. + */ + llc_data->u->xid_tag |= 0x00000001L << XID_T200; + } + else /* SGSN_RESPONSE */ + { + /* + * Sense of negotiation-correctness of response has already been checked + * in u_check_xid(). + */ + llc_data->t200->length = XID2INT (llc_data->decoded_xid.t200.value); + } + + /* + * Remove tag in xid_tag_negotiate, because this parameter has been + * explicitly negotiated by the peer. + * <R.LLC.XIDNEG_R.A.015> + */ + llc_data->u->xid_tag_negotiate &= ~(0x00000001L << XID_T200); + } + + + /************************************************************************** + * N200: + * Can be negotiated in state 'ADM' and 'ABM'. + * <R.LLC.XID_PAR.A.006> + */ + if (llc_data->decoded_xid.n200.valid) + { + if (cr_bit EQ SGSN_COMMAND) + { + if (llc_xid_value_acceptable (llc_data->current_sapi, XID_N200, + llc_data->decoded_xid.n200.value)) + { + *(llc_data->n200) = llc_data->decoded_xid.n200.value; + } + else /* value is not acceptable */ + { + /* + * Sense of negotiation down: offer lower value; sense of + * negotiation up: offer higher value. + * This happens automatically by just responding with our current + * global value. + */ + } + + /* + * In case we also want to negotiate this value, but to a lower! + * value, delete this parameter from the requested XID structure + */ + if (llc_data->u->requested_xid.n200.valid AND + llc_data->u->requested_xid.n200.value <= llc_data->decoded_xid.n200.value) + { + llc_data->u->requested_xid.n200.valid = FALSE; + } + + /* + * Tag parameter for next response. + */ + llc_data->u->xid_tag |= 0x00000001L << XID_N200; + } + else /* SGSN_RESPONSE */ + { + /* + * Sense of negotiation-correctness of response has already been checked + * in u_check_xid(). + */ + *(llc_data->n200) = llc_data->decoded_xid.n200.value; + } + + /* + * Remove tag in xid_tag_negotiate, because this parameter has been + * explicitly negotiated by the peer. + * <R.LLC.XIDNEG_R.A.015> + */ + llc_data->u->xid_tag_negotiate &= ~(0x00000001L << XID_N200); + } + + + /************************************************************************** + * N201-U: + * Can be negotiated in state 'ADM' and 'ABM'. If N201-U is negotiated to a + * lower value than previously used, then any queued or new U and UI frames + * that violate the new value of N201-U should be discarded and not + * transmitted. + * <R.LLC.XID_PAR.A.006>, <R.LLC.XID_PAR.A.010> + */ + if (llc_data->decoded_xid.n201_u.valid) + { + if (cr_bit EQ SGSN_COMMAND) + { + if (llc_xid_value_acceptable (llc_data->current_sapi, XID_N201_U, + llc_data->decoded_xid.n201_u.value)) + { + if (llc_data->decoded_xid.n201_u.value NEQ *(llc_data->n201_u)) + { + *(llc_data->n201_u) = llc_data->decoded_xid.n201_u.value; + + /* + * LL_XID_IND has to be sent to layer 3. + */ + *xid_ind = TRUE; + } + } + else /* value is not acceptable */ + { + /* + * Sense of negotiation down: offer lower value; sense of + * negotiation up: offer higher value. + * This happens automatically by just responding with our current + * global value. + */ + } + + /* + * In case we also want to negotiate this value, but to a higher + * value, delete this parameter from the requested XID structure + */ + if (llc_data->u->requested_xid.n201_u.valid AND + llc_data->u->requested_xid.n201_u.value >= llc_data->decoded_xid.n201_u.value) + { + llc_data->u->requested_xid.n201_u.valid = FALSE; + } + + /* + * Tag parameter for next response. + */ + llc_data->u->xid_tag |= 0x00000001L << XID_N201_U; + } + else /* SGSN_RESPONSE */ + { + if (llc_data->decoded_xid.n201_u.value NEQ *(llc_data->n201_u)) + { + /* + * Sense of negotiation-correctness of response has already been checked + * in u_check_xid(). + */ + *(llc_data->n201_u) = llc_data->decoded_xid.n201_u.value; + + /* + * LL_XID_IND has to be sent to layer 3. + */ + *xid_ind = TRUE; + } + } + + /* + * Remove tag in xid_tag_negotiate, because this parameter has been + * explicitly negotiated by the peer. + * <R.LLC.XIDNEG_R.A.015> + */ + llc_data->u->xid_tag_negotiate &= ~(0x00000001L << XID_N201_U); + } + + + /************************************************************************** + * N201-I: + * N201-I can be negotiated to any value in state 'ADM'. It can only be + * negotiated to the same or higher value as previously used in state 'ABM'. + * <R.LLC.XID_PAR.A.008>, <R.LLC.XID_PAR.A.009> + */ + if (llc_data->decoded_xid.n201_i.valid) + { + if (cr_bit EQ SGSN_COMMAND) + { + if (llc_xid_value_acceptable (llc_data->current_sapi, XID_N201_I, + llc_data->decoded_xid.n201_i.value)) + { + if (((GET_STATE (U) EQ U_ABM) AND + (llc_data->decoded_xid.n201_i.value > *(llc_data->n201_i))) + OR /* state 'ADM' */ + (llc_data->decoded_xid.n201_i.value NEQ *(llc_data->n201_i))) + { + *(llc_data->n201_i) = llc_data->decoded_xid.n201_i.value; + + /* + * LL_XID_IND has to be sent to layer 3. + */ + *xid_ind = TRUE; + } + } + else /* value is not acceptable */ + { + /* + * Sense of negotiation down: offer lower value; sense of + * negotiation up: offer higher value. + * This happens automatically by just responding with our current + * global value. + */ + } + + /* + * In case we also want to negotiate this value, but to a higher + * value, delete this parameter from the requested XID structure + */ + + if (llc_data->u->requested_xid.n201_i.valid AND + llc_data->u->requested_xid.n201_i.value >= llc_data->decoded_xid.n201_i.value) + { + llc_data->u->requested_xid.n201_i.valid = FALSE; + } + + /* + * Tag parameter for next response. + */ + llc_data->u->xid_tag |= 0x00000001L << XID_N201_I; + } + else /* SGSN_RESPONSE */ + { + /* + * Sense of negotiation-correctness of response has already been checked + * in u_check_xid(). + */ + if (((GET_STATE (U) EQ U_ABM) AND + (llc_data->decoded_xid.n201_i.value > *(llc_data->n201_i))) + OR /* state 'ADM' */ + (llc_data->decoded_xid.n201_i.value NEQ *(llc_data->n201_i))) + { + *(llc_data->n201_i) = llc_data->decoded_xid.n201_i.value; + + /* + * LL_XID_IND has to be sent to layer 3. + */ + *xid_ind = TRUE; + } + } + + /* + * Remove tag in xid_tag_negotiate, because this parameter has been + * explicitly negotiated by the peer. + * <R.LLC.XIDNEG_R.A.015> + */ + llc_data->u->xid_tag_negotiate &= ~(0x00000001L << XID_N201_I); + } + + + /************************************************************************** + * mD: + * mD can be negotiated to any value in state 'ADM'. It can only be + * negotiated to the same or higher value as previously used in state 'ABM'. + * <R.LLC.XID_PAR.A.008>, <R.LLC.XID_PAR.A.009> + */ + if (llc_data->decoded_xid.md.valid) + { + if (cr_bit EQ SGSN_COMMAND) + { + if (llc_xid_value_acceptable (llc_data->current_sapi, XID_MD, + llc_data->decoded_xid.md.value)) + { + if (((GET_STATE (U) EQ U_ABM) AND + (llc_data->decoded_xid.md.value > *(llc_data->md))) + OR /* state 'ADM' */ + (llc_data->decoded_xid.md.value NEQ *(llc_data->md))) + { + *(llc_data->md) = llc_data->decoded_xid.md.value; + } + } + else /* value is not acceptable */ + { + /* + * Sense of negotiation down: offer lower value; sense of + * negotiation up: offer higher value. + * This happens automatically by just responding with our current + * global value. + */ + } + + /* + * In case we also want to negotiate this value, but to a higher + * value, delete this parameter from the requested XID structure + */ + if (llc_data->u->requested_xid.md.valid AND + llc_data->u->requested_xid.md.value >= llc_data->decoded_xid.md.value) + { + llc_data->u->requested_xid.md.valid = FALSE; + } + + /* + * Tag parameter for next response. + */ + llc_data->u->xid_tag |= 0x00000001L << XID_MD; + } + else /* SGSN_RESPONSE */ + { + /* + * Sense of negotiation-correctness of response has already been checked + * in u_check_xid(). + */ + if (((GET_STATE (U) EQ U_ABM) AND + (llc_data->decoded_xid.md.value > *(llc_data->md))) + OR /* state 'ADM' */ + (llc_data->decoded_xid.md.value NEQ *(llc_data->md))) + { + *(llc_data->md) = llc_data->decoded_xid.md.value; + } + } + + /* + * Remove tag in xid_tag_negotiate, because this parameter has been + * explicitly negotiated by the peer. + * <R.LLC.XIDNEG_R.A.015> + */ + llc_data->u->xid_tag_negotiate &= ~(0x00000001L << XID_MD); + } + + + /************************************************************************** + * mU: + * mU can be negotiated to any value in state 'ADM'. It can only be + * negotiated to the same or higher value as previously used in state 'ABM'. + * <R.LLC.XID_PAR.A.008>, <R.LLC.XID_PAR.A.009> + */ + if (llc_data->decoded_xid.mu.valid) + { + if (cr_bit EQ SGSN_COMMAND) + { + if (llc_xid_value_acceptable (llc_data->current_sapi, XID_MU, + llc_data->decoded_xid.mu.value)) + { + if (((GET_STATE (U) EQ U_ABM) AND + (llc_data->decoded_xid.mu.value > *(llc_data->mu))) + OR /* state 'ADM' */ + (llc_data->decoded_xid.mu.value NEQ *(llc_data->mu))) + { + *(llc_data->mu) = llc_data->decoded_xid.mu.value; + } + } + else /* value is not acceptable */ + { + /* + * Sense of negotiation down: offer lower value; sense of + * negotiation up: offer higher value. + * This happens automatically by just responding with our current + * global value. + */ + } + + /* + * In case we also want to negotiate this value, but to a higher + * value, delete this parameter from the requested XID structure + */ + if (llc_data->u->requested_xid.mu.valid AND + llc_data->u->requested_xid.mu.value >= llc_data->decoded_xid.mu.value) + { + llc_data->u->requested_xid.mu.valid = FALSE; + } + + /* + * Tag parameter for next response. + */ + llc_data->u->xid_tag |= 0x00000001L << XID_MU; + } + else /* SGSN_RESPONSE */ + { + /* + * Sense of negotiation-correctness of response has already been checked + * in u_check_xid(). + */ + if (((GET_STATE (U) EQ U_ABM) AND + (llc_data->decoded_xid.mu.value > *(llc_data->mu))) + OR /* state 'ADM' */ + (llc_data->decoded_xid.mu.value NEQ *(llc_data->mu))) + { + *(llc_data->mu) = llc_data->decoded_xid.mu.value; + } + } + + /* + * Remove tag in xid_tag_negotiate, because this parameter has been + * explicitly negotiated by the peer. + * <R.LLC.XIDNEG_R.A.015> + */ + llc_data->u->xid_tag_negotiate &= ~(0x00000001L << XID_MU); + } + + + /************************************************************************** + * kD: + * kD can be negotiated to any value in state 'ADM'. It can only be + * negotiated to the same or higher value as previously used in state 'ABM'. + * <R.LLC.XID_PAR.A.008>, <R.LLC.XID_PAR.A.009> + */ + if (llc_data->decoded_xid.kd.valid) + { + if (cr_bit EQ SGSN_COMMAND) + { + if (llc_xid_value_acceptable (llc_data->current_sapi, XID_KD, + llc_data->decoded_xid.kd.value)) + { + if (((GET_STATE (U) EQ U_ABM) AND + (llc_data->decoded_xid.kd.value > *(llc_data->kd))) + OR /* state 'ADM' */ + (llc_data->decoded_xid.kd.value NEQ *(llc_data->kd))) + { + *(llc_data->kd) = llc_data->decoded_xid.kd.value; + } + } + else /* value is not acceptable */ + { + /* + * Sense of negotiation down: offer lower value; sense of + * negotiation up: offer higher value. + * This happens automatically by just responding with our current + * global value. + */ + } + + /* + * In case we also want to negotiate this value, but to a higher + * value, delete this parameter from the requested XID structure + */ + if (llc_data->u->requested_xid.kd.valid AND + llc_data->u->requested_xid.kd.value >= llc_data->decoded_xid.kd.value) + { + llc_data->u->requested_xid.kd.valid = FALSE; + } + + /* + * Tag parameter for next response. + */ + llc_data->u->xid_tag |= 0x00000001L << XID_KD; + } + else /* SGSN_RESPONSE */ + { + /* + * Sense of negotiation-correctness of response has already been checked + * in u_check_xid(). + */ + if (((GET_STATE (U) EQ U_ABM) AND + (llc_data->decoded_xid.kd.value > *(llc_data->kd))) + OR /* state 'ADM' */ + (llc_data->decoded_xid.kd.value NEQ *(llc_data->kd))) + { + *(llc_data->kd) = llc_data->decoded_xid.kd.value; + } + } + + /* + * Remove tag in xid_tag_negotiate, because this parameter has been + * explicitly negotiated by the peer. + * <R.LLC.XIDNEG_R.A.015> + */ + llc_data->u->xid_tag_negotiate &= ~(0x00000001L << XID_KD); + } + + + /************************************************************************** + * kU: + * kU can be negotiated to any value in state 'ADM'. It can only be + * negotiated to the same or higher value as previously used in state 'ABM'. + * <R.LLC.XID_PAR.A.008>, <R.LLC.XID_PAR.A.009> + */ + if (llc_data->decoded_xid.ku.valid) + { + if (cr_bit EQ SGSN_COMMAND) + { + if (llc_xid_value_acceptable (llc_data->current_sapi, XID_KU, + llc_data->decoded_xid.ku.value)) + { + if (((GET_STATE (U) EQ U_ABM) AND + (llc_data->decoded_xid.ku.value > *(llc_data->ku))) + OR /* state 'ADM' */ + (llc_data->decoded_xid.ku.value NEQ *(llc_data->ku))) + { + *(llc_data->ku) = llc_data->decoded_xid.ku.value; + } + } + else /* value is not acceptable */ + { + /* + * Sense of negotiation down: offer lower value; sense of + * negotiation up: offer higher value. + * This happens automatically by just responding with our current + * global value. + */ + } + + /* + * In case we also want to negotiate this value, but to a higher + * value, delete this parameter from the requested XID structure + */ + if (llc_data->u->requested_xid.ku.valid AND + llc_data->u->requested_xid.ku.value >= llc_data->decoded_xid.ku.value) + { + llc_data->u->requested_xid.ku.valid = FALSE; + } + + /* + * Tag parameter for next response. + */ + llc_data->u->xid_tag |= 0x00000001L << XID_KU; + } + else /* SGSN_RESPONSE */ + { + /* + * Sense of negotiation-correctness of response has already been checked + * in u_check_xid(). + */ + if (((GET_STATE (U) EQ U_ABM) AND + (llc_data->decoded_xid.ku.value > *(llc_data->ku))) + OR /* state 'ADM' */ + (llc_data->decoded_xid.ku.value NEQ *(llc_data->ku))) + { + *(llc_data->ku) = llc_data->decoded_xid.ku.value; + } + } + + /* + * Remove tag in xid_tag_negotiate, because this parameter has been + * explicitly negotiated by the peer. + * <R.LLC.XIDNEG_R.A.015> + */ + llc_data->u->xid_tag_negotiate &= ~(0x00000001L << XID_KU); + } + + + /************************************************************************** + * Layer 3 stuff + */ + if (llc_data->decoded_l3_xid.valid) + { + /* + * LL_XID_IND has to be sent to layer 3. + */ + *xid_ind = TRUE; + + if (cr_bit EQ SGSN_COMMAND) + { + /* + * Tag parameter for next response. + */ + llc_data->u->xid_tag |= 0x00000001L << XID_LAYER_3; + } + else /* SGSN_RESPONSE */ + { + /* + * Reset valid flag of parameter in requested_xid because it has been + * included in the response. + */ + llc_data->requested_l3_xid->valid = FALSE; + } + + /* + * Remove tag in xid_tag_negotiate, because this parameter has been + * explicitly negotiated by the peer. + * <R.LLC.XIDNEG_R.A.015> + */ + llc_data->u->xid_tag_negotiate &= ~(0x00000001L << XID_LAYER_3); + } + + return; /* u_eval_xid */ +} + + +/* ++------------------------------------------------------------------------------ +| Function : u_insert_xid ++------------------------------------------------------------------------------ +| Description : This procedure inserts (i.e. appends) the XID parameters that +| are tagged in llc_xid_tag. This variable is normally set in +| u_eval_xid(). The parameter cr_bit indicates if the XID +| information field is written in a command or a response frame. +| The values of the parameters are taken from llc_requested_xid +| if marked as valid (normally the cause when a command frame is +| to be sent), otherwise from global variables (the normal cause +| for a response frame). If a response frame is to be sent, and +| the parameter is tagged in llc_xid_tag, but has not been +| received in the command frame (as indicated by +| llc_decoded_xid), the parameter is tagged in llc_xid_negotiate. +| Each parameter is tagged in llc_xid_tag_sent for collision +| checks and the like +| +| Parameters : sdu - a valid pointer to an SDU, containing enough octets +| for the tagged number of XID parameters +| cr_bit - MS_COMMAND/_RESPONSE is to be sent +| ++------------------------------------------------------------------------------ +*/ +GLOBAL void u_insert_xid +( +#ifdef LL_DESC + T_desc_list3 *desc_list3, +#else + T_sdu *sdu, +#endif + T_BIT cr_bit +) +{ + UBYTE *data; + ULONG value; +#ifdef LL_DESC + UBYTE *desc_buf; + T_desc3 *desc3; +#endif + TRACE_FUNCTION( "u_insert_xid" ); + + /* + * Set data to point to the first free data octet in sdu. data has to + * be incremented with each octet that is written in sdu to ensure that + * it always points to the first free data octet. + */ +#ifdef LL_DESC + desc3 = (T_desc3*) desc_list3->first; + desc_buf = (UBYTE*)desc3->buffer; + data = &desc_buf[(desc3->offset)+(desc3->len)]; +#else + data = &sdu->buf[(sdu->o_buf/8)+(sdu->l_buf/8)]; +#endif + /* + * Preset global XID sent indicator for next response. + */ + llc_data->u->xid_tag_sent = 0L; + + + /************************************************************************* + * Version: + * Shall not be negotiated while in state 'ABM'. + * <R.LLC.XID_PAR.A.001> + */ + if (llc_data->u->xid_tag & (0x00000001L << XID_VERSION) AND + (GET_STATE (U) NEQ U_ABM)) + { + if (cr_bit EQ MS_COMMAND) + { + value = llc_data->u->requested_xid.version.value; + } + else + { + if (llc_data->u->requested_xid.version.valid) + { + llc_data->version = llc_data->u->requested_xid.version.value; + } + + value = llc_data->version; + + /* + * Tag a parameter for further negotiation that was not included in + * the XID command in every XID response until the parameter has been + * explicitly negotiated, either by responding to an XID command that + * included the parameter, or by explicitly including the parameter + * the next time an XID command is transmitted. + * <R.LLC.XIDNEG_R.A.015> + */ + if (!llc_data->decoded_xid.version.valid) + { + llc_data->u->xid_tag_negotiate |= 0x00000001L << XID_VERSION; + } + } + + /* + * Append determined value to SDU data, increment SDU data pointer to + * point to the first free data octet again. + */ + *data++ = (UBYTE)0x00 | ((UBYTE)XID_VERSION << 2) | (UBYTE)XID_VERSION_LEN; + *data++ = (UBYTE)value; + +#ifdef LL_DESC + desc3->len += (XID_VERSION_LEN + 1); +#else + sdu->l_buf += (XID_VERSION_LEN + 1) * 8; +#endif + /* + * Tag parameter for collision checks and the like. + */ + llc_data->u->xid_tag_sent |= 0x00000001L << XID_VERSION; + } + + /************************************************************************* + * T200: + * Can be negotiated in state 'ADM' and 'ABM'. + * <R.LLC.XID_PAR.A.006> + */ + if (llc_data->u->xid_tag & (0x00000001L << XID_T200)) + { + if (cr_bit EQ MS_COMMAND) + { + value = llc_data->u->requested_xid.t200.value; + } + else + { + if (llc_data->u->requested_xid.t200.valid) + { + llc_data->t200->length = XID2INT(llc_data->u->requested_xid.t200.value); + } + + value = INT2XID(llc_data->t200->length); + + /* + * Tag a parameter for further negotiation that was not included in + * the XID command in every XID response until the parameter has been + * explicitly negotiated, either by responding to an XID command that + * included the parameter, or by explicitly including the parameter + * the next time an XID command is transmitted. + * <R.LLC.XIDNEG_R.A.015> + */ + if (!llc_data->decoded_xid.t200.valid) + { + llc_data->u->xid_tag_negotiate |= 0x00000001L << XID_T200; + } + } + + /* + * Append determined value to SDU data, increment SDU data pointer to + * point to the first free data octet again. + */ + *data++ = (UBYTE)0x00 | ((UBYTE)XID_T200 << 2) | (UBYTE)XID_T200_LEN; + *data++ = (UBYTE)(value >> 8); + *data++ = (UBYTE)value; + +#ifdef LL_DESC + desc3->len += (XID_T200_LEN + 1); +#else + sdu->l_buf += (XID_T200_LEN + 1) * 8; +#endif + + /* + * Tag parameter for collision checks and the like. + */ + llc_data->u->xid_tag_sent |= 0x00000001L << XID_T200; + } + + /************************************************************************* + * N200: + * Can be negotiated in state 'ADM' and 'ABM'. + * <R.LLC.XID_PAR.A.006> + */ + if (llc_data->u->xid_tag & (0x00000001L << XID_N200)) + { + if (cr_bit EQ MS_COMMAND) + { + value = llc_data->u->requested_xid.n200.value; + } + else + { + if (llc_data->u->requested_xid.n200.valid) + { + *(llc_data->n200) = llc_data->u->requested_xid.n200.value; + } + + value = *(llc_data->n200); + + /* + * Tag a parameter for further negotiation that was not included in + * the XID command in every XID response until the parameter has been + * explicitly negotiated, either by responding to an XID command that + * included the parameter, or by explicitly including the parameter + * the next time an XID command is transmitted. + * <R.LLC.XIDNEG_R.A.015> + */ + if (!llc_data->decoded_xid.n200.valid) + { + llc_data->u->xid_tag_negotiate |= 0x00000001L << XID_N200; + } + } + + /* + * Append determined value to SDU data, increment SDU data pointer to + * point to the first free data octet again. + */ + *data++ = (UBYTE)0x00 | ((UBYTE)XID_N200 << 2) | (UBYTE)XID_N200_LEN; + *data++ = (UBYTE)value; + +#ifdef LL_DESC + desc3->len += (XID_N200_LEN + 1); +#else + sdu->l_buf += (XID_N200_LEN + 1) * 8; +#endif + + /* + * Tag parameter for collision checks and the like. + */ + llc_data->u->xid_tag_sent |= 0x00000001L << XID_N200; + } + + /************************************************************************* + * N201-U: + * Can be negotiated in state 'ADM' and 'ABM'. + * <R.LLC.XID_PAR.A.006> + */ + if (llc_data->u->xid_tag & (0x00000001L << XID_N201_U)) + { + if (cr_bit EQ MS_COMMAND) + { + value = llc_data->u->requested_xid.n201_u.value; + } + else + { + if (llc_data->u->requested_xid.n201_u.valid) + { + *(llc_data->n201_u) = llc_data->u->requested_xid.n201_u.value; + } + + value = *(llc_data->n201_u); + + /* + * Tag a parameter for further negotiation that was not included in + * the XID command in every XID response until the parameter has been + * explicitly negotiated, either by responding to an XID command that + * included the parameter, or by explicitly including the parameter + * the next time an XID command is transmitted. + * <R.LLC.XIDNEG_R.A.015> + */ + if (!llc_data->decoded_xid.n201_u.valid) + { + llc_data->u->xid_tag_negotiate |= 0x00000001L << XID_N201_U; + } + } + + /* + * Append determined value to SDU data, increment SDU data pointer to + * point to the first free data octet again. + */ + *data++ = (UBYTE)0x00 | ((UBYTE)XID_N201_U << 2) | (UBYTE)XID_N201_U_LEN; + *data++ = (UBYTE)(value >> 8); + *data++ = (UBYTE)value; + +#ifdef LL_DESC + desc3->len += (XID_N201_U_LEN + 1); +#else + sdu->l_buf += (XID_N201_U_LEN + 1) * 8; +#endif + + /* + * Tag parameter for collision checks and the like. + */ + llc_data->u->xid_tag_sent |= 0x00000001L << XID_N201_U; + } + + /************************************************************************* + * N201-I: + * Can be negotiated in state 'ADM' and 'ABM'. + * <R.LLC.XID_PAR.A.006> + */ + if (llc_data->u->xid_tag & (0x00000001L << XID_N201_I)) + { + if (cr_bit EQ MS_COMMAND) + { + value = llc_data->u->requested_xid.n201_i.value; + } + else + { + if (llc_data->u->requested_xid.n201_i.valid) + { + *(llc_data->n201_i) = llc_data->u->requested_xid.n201_i.value; + } + + value = *(llc_data->n201_i); + + /* + * Tag a parameter for further negotiation that was not included in + * the XID command in every XID response until the parameter has been + * explicitly negotiated, either by responding to an XID command that + * included the parameter, or by explicitly including the parameter + * the next time an XID command is transmitted. + * <R.LLC.XIDNEG_R.A.015> + */ + if (!llc_data->decoded_xid.n201_i.valid) + { + llc_data->u->xid_tag_negotiate |= 0x00000001L << XID_N201_I; + } + } + + /* + * Append determined value to SDU data, increment SDU data pointer to + * point to the first free data octet again. + */ + *data++ = (UBYTE)0x00 | ((UBYTE)XID_N201_I << 2) | (UBYTE)XID_N201_I_LEN; + *data++ = (UBYTE)(value >> 8); + *data++ = (UBYTE)value; + +#ifdef LL_DESC + desc3->len += (XID_N201_I_LEN + 1); +#else + sdu->l_buf += (XID_N201_I_LEN + 1) * 8; +#endif + + /* + * Tag parameter for collision checks and the like. + */ + llc_data->u->xid_tag_sent |= 0x00000001L << XID_N201_I; + } + + /************************************************************************* + * md: + * Can be negotiated in state 'ADM' and 'ABM'. + * <R.LLC.XID_PAR.A.006> + */ + if (llc_data->u->xid_tag & (0x00000001L << XID_MD)) + { + if (cr_bit EQ MS_COMMAND) + { + value = llc_data->u->requested_xid.md.value; + } + else + { + if (llc_data->u->requested_xid.md.valid) + { + *(llc_data->md) = llc_data->u->requested_xid.md.value; + } + + value = *(llc_data->md); + + /* + * Tag a parameter for further negotiation that was not included in + * the XID command in every XID response until the parameter has been + * explicitly negotiated, either by responding to an XID command that + * included the parameter, or by explicitly including the parameter + * the next time an XID command is transmitted. + * <R.LLC.XIDNEG_R.A.015> + */ + if (!llc_data->decoded_xid.md.valid) + { + llc_data->u->xid_tag_negotiate |= 0x00000001L << XID_MD; + } + } + + /* + * Append determined value to SDU data, increment SDU data pointer to + * point to the first free data octet again. + */ + *data++ = (UBYTE)0x00 | ((UBYTE)XID_MD << 2) | (UBYTE)XID_MD_LEN; + *data++ = (UBYTE)(value >> 8); + *data++ = (UBYTE)value; + +#ifdef LL_DESC + desc3->len += (XID_MD_LEN + 1); +#else + sdu->l_buf += (XID_MD_LEN + 1) * 8; +#endif + + /* + * Tag parameter for collision checks and the like. + */ + llc_data->u->xid_tag_sent |= 0x00000001L << XID_MD; + } + + /************************************************************************* + * mu: + * Can be negotiated in state 'ADM' and 'ABM'. + * <R.LLC.XID_PAR.A.006> + */ + if (llc_data->u->xid_tag & (0x00000001L << XID_MU)) + { + if (cr_bit EQ MS_COMMAND) + { + value = llc_data->u->requested_xid.mu.value; + } + else + { + if (llc_data->u->requested_xid.mu.valid) + { + *(llc_data->mu) = llc_data->u->requested_xid.mu.value; + } + + value = *(llc_data->mu); + + /* + * Tag a parameter for further negotiation that was not included in + * the XID command in every XID response until the parameter has been + * explicitly negotiated, either by responding to an XID command that + * included the parameter, or by explicitly including the parameter + * the next time an XID command is transmitted. + * <R.LLC.XIDNEG_R.A.015> + */ + if (!llc_data->decoded_xid.mu.valid) + { + llc_data->u->xid_tag_negotiate |= 0x00000001L << XID_MU; + } + } + + /* + * Append determined value to SDU data, increment SDU data pointer to + * point to the first free data octet again. + */ + *data++ = (UBYTE)0x00 | ((UBYTE)XID_MU << 2) | (UBYTE)XID_MU_LEN; + *data++ = (UBYTE)(value >> 8); + *data++ = (UBYTE)value; + +#ifdef LL_DESC + desc3->len += (XID_MU_LEN + 1); +#else + sdu->l_buf += (XID_MU_LEN + 1) * 8; +#endif + + /* + * Tag parameter for collision checks and the like. + */ + llc_data->u->xid_tag_sent |= 0x00000001L << XID_MU; + } + + /************************************************************************* + * kd: + * Can be negotiated in state 'ADM' and 'ABM'. + * <R.LLC.XID_PAR.A.006> + */ + if (llc_data->u->xid_tag & (0x00000001L << XID_KD)) + { + if (cr_bit EQ MS_COMMAND) + { + value = llc_data->u->requested_xid.kd.value; + } + else + { + if (llc_data->u->requested_xid.kd.valid) + { + *(llc_data->kd) = llc_data->u->requested_xid.kd.value; + } + + value = *(llc_data->kd); + + /* + * Tag a parameter for further negotiation that was not included in + * the XID command in every XID response until the parameter has been + * explicitly negotiated, either by responding to an XID command that + * included the parameter, or by explicitly including the parameter + * the next time an XID command is transmitted. + * <R.LLC.XIDNEG_R.A.015> + */ + if (!llc_data->decoded_xid.kd.valid) + { + llc_data->u->xid_tag_negotiate |= 0x00000001L << XID_KD; + } + } + + /* + * Append determined value to SDU data, increment SDU data pointer to + * point to the first free data octet again. + */ + *data++ = (UBYTE)0x00 | ((UBYTE)XID_KD << 2) | (UBYTE)XID_KD_LEN; + *data++ = (UBYTE)value; + +#ifdef LL_DESC + desc3->len += (XID_KD_LEN + 1); +#else + sdu->l_buf += (XID_KD_LEN + 1) * 8; +#endif + + /* + * Tag parameter for collision checks and the like. + */ + llc_data->u->xid_tag_sent |= 0x00000001L << XID_KD; + } + + /************************************************************************* + * ku: + * Can be negotiated in state 'ADM' and 'ABM'. + * <R.LLC.XID_PAR.A.006> + */ + if (llc_data->u->xid_tag & (0x00000001L << XID_KU)) + { + if (cr_bit EQ MS_COMMAND) + { + value = llc_data->u->requested_xid.ku.value; + } + else + { + if (llc_data->u->requested_xid.ku.valid) + { + *(llc_data->ku) = llc_data->u->requested_xid.ku.value; + } + + value = *(llc_data->ku); + + /* + * Tag a parameter for further negotiation that was not included in + * the XID command in every XID response until the parameter has been + * explicitly negotiated, either by responding to an XID command that + * included the parameter, or by explicitly including the parameter + * the next time an XID command is transmitted. + * <R.LLC.XIDNEG_R.A.015> + */ + if (!llc_data->decoded_xid.ku.valid) + { + llc_data->u->xid_tag_negotiate |= 0x00000001L << XID_KU; + } + } + + /* + * Append determined value to SDU data, increment SDU data pointer to + * point to the first free data octet again. + */ + *data++ = (UBYTE)0x00 | ((UBYTE)XID_KU << 2) | (UBYTE)XID_KU_LEN; + *data++ = (UBYTE)value; + +#ifdef LL_DESC + desc3->len += (XID_KU_LEN + 1); +#else + sdu->l_buf += (XID_KU_LEN + 1) * 8; +#endif + + /* + * Tag parameter for collision checks and the like. + */ + llc_data->u->xid_tag_sent |= 0x00000001L << XID_KU; + } + + /************************************************************************* + * Insert Layer 3 (sense of negotiation: both) + */ + if (llc_data->u->xid_tag & (0x00000001L << XID_LAYER_3)) + { + if (llc_data->requested_l3_xid->valid) + { + /* + * Write two bytes header + */ + *data++ = (UBYTE)0x80 /* = set length to 8 bit */ | ((UBYTE)XID_LAYER_3 << 2) + | ((UBYTE)((llc_data->requested_l3_xid->length >> 6) & 0x03)); + *data++ = (UBYTE)(llc_data->requested_l3_xid->length << 2); + + /* + * Copy data + */ + memcpy (data, llc_data->requested_l3_xid->value, + llc_data->requested_l3_xid->length); + + /* + * Increase DESC/SDU size + */ +#ifdef LL_DESC + desc3->len += (llc_data->requested_l3_xid->length + 2); +#else + sdu->l_buf += (llc_data->requested_l3_xid->length + 2) * 8; +#endif + /* + * Tag parameter for collision checks and the like. + */ + llc_data->u->xid_tag_sent |= 0x00000001L << XID_LAYER_3; + } + } + +#ifdef LL_DESC + desc_list3->list_len = desc3->len; +#endif /* LL_DESC */ + +#ifdef TRACE_EVE +#ifdef LL_DESC + { + int i; + int l = desc3->len; + int o = desc3->offset; + + vsi_o_ttrace(VSI_CALLER TC_EVENT, "ul XID sdu dump len:%d bytes", l); + + for (i=0; i<l; i+=10) + { + vsi_o_ttrace(VSI_CALLER TC_EVENT, "%.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x", + desc_buf[o+i+0], desc_buf[o+i+1], + desc_buf[o+i+2], desc_buf[o+i+3], desc_buf[o+i+4], + + desc_buf[o+i+5], desc_buf[o+i+6], + desc_buf[o+i+7], desc_buf[o+i+8], desc_buf[o+i+9]); + } + } + +#else + { + int i; + int l = sdu->l_buf/8; + int o = sdu->o_buf/8; + + vsi_o_ttrace(VSI_CALLER TC_EVENT, "ul XID sdu dump len:%d bytes", l); + + for (i=0; i<l; i+=10) + { + vsi_o_ttrace(VSI_CALLER TC_EVENT, "%.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x", + sdu->buf[o+i+0], sdu->buf[o+i+1], + sdu->buf[o+i+2], sdu->buf[o+i+3], sdu->buf[o+i+4], + + sdu->buf[o+i+5], sdu->buf[o+i+6], + sdu->buf[o+i+7], sdu->buf[o+i+8], sdu->buf[o+i+9]); + } + } + +#endif /* LL_DESC */ +#endif /* TRACE_EVE */ + /* + * If we have build an response including all XID parameters, + * we can clean all the xid_tags and layer 3 parameters. + */ + if (cr_bit EQ MS_RESPONSE) + { + llc_data->u->xid_tag = 0; + llc_data->requested_l3_xid->valid = FALSE; + } + + return; +} /* u_insert_xid() */ + + +/* ++------------------------------------------------------------------------------ +| Function : u_insert_frmr_information ++------------------------------------------------------------------------------ +| Description : This procedure inserts (i.e. appends) an FRMR information +| field into the given sdu. frame is the rejected frame of which +| the control field is included (ctrl_length is truncated to a +| maximum of six octets; if it is not known, length is +| determinated from pdu_type), v_s and v_r are the current +| send and receive state numbers of ITX/IRX, cr_bit indicates +| if the frame was a command or response (SGSN_COMMAND/RESPONSE), +| and reason indicates the reason of the frame rejection +| condition (the lower nibble of reason is equivalent to W4-W1). +| +| Parameters : sdu - a valid pointer to an SDU, containing enough +| octets for the FRMR information field +| (U_FRMR_INFO_SIZE) +| frame - frame that caused the frame rejection condition +| pdu_type - frame type +| ctrl_length - control field length, if known +| v_s - current V(S) +| v_r - current V(R) +| cr_bit - setting of C/R bit in frame +| reason - reason of the frame rejection condition (lower +| nibble conforms to W4-W1 in FRMR response) +| ++------------------------------------------------------------------------------ +*/ +GLOBAL void u_insert_frmr_information +( +#ifndef LL_DESC + T_sdu *sdu, +#else + T_desc_list3 *desc_list3, +#endif + T_LL_UNITDATA_IND *frame, + T_PDU_TYPE pdu_type, + USHORT ctrl_length, + T_FRAME_NUM v_s, + T_FRAME_NUM v_r, + T_BIT cr_bit, + UBYTE reason +) +{ + +#ifndef LL_DESC + UBYTE *sdu_data; +#else + UBYTE *help, *desc_data; + T_desc3 *desc3; +#endif + UBYTE *ctrl; + USHORT octet; + + + TRACE_FUNCTION( "u_insert_frmr_information" ); + +#ifndef LL_DESC + /* + * Set sdu_data to point to the first free data octet in sdu. sdu_data + * has to be incremented with each octet that is written in sdu to ensure + * that it always points to the first free data octet. + */ + sdu_data = &sdu->buf[(sdu->o_buf/8)+(sdu->l_buf/8)]; + + /* + * Adjust length of SDU to include fixed sized FRMR information field. + */ + sdu->l_buf += U_FRMR_INFO_SIZE * 8; + + /* + * Set ctrl to point to the first octet of the faulty frame control field. + */ + ctrl = &frame->sdu.buf[(frame->sdu.o_buf/8)+1]; + + /* + * If ctrl_length is unknown, the frame type is known, therefore the length + * of the control field can be easily determined. + */ + if (ctrl_length EQ FRMR_CTRL_LENGTH_UNKNOWN) + { + if (pdu_type EQ I_FRAME) + { + ctrl_length = I_CTRL_OCTETS; + } + else if (pdu_type EQ S_FRAME) + { + ctrl_length = S_CTRL_OCTETS; + } + else if (pdu_type EQ UI_FRAME) + { + ctrl_length = UI_CTRL_OCTETS; + } + else if (pdu_type EQ U_FRAME) + { + ctrl_length = U_CTRL_OCTETS; + } + else + { + /* + * Undefined control field type, set control field length to SDU length + * minus address field, limited to six octets. + */ + TRACE_ERROR ("undefined control field type"); + ctrl_length = (sdu->l_buf/8 - 1) < 6 ? (sdu->l_buf/8 - 1) : 6; + } + } + else /* ctrl_length is already known */ + { + /* + * Limit control field length to six octets, according to 04.64. + */ + ctrl_length = ctrl_length < 6 ? ctrl_length : 6; + } + + + /* + * Copy the first six octets of faulty control field. If the control field + * is smaller that six octets, the unused octets are set to 0. + */ + for (octet = 0; octet < 6; octet++) + { + if (octet < ctrl_length) + { + sdu_data[octet] = ctrl[octet]; + } + else + { + sdu_data[octet] = 0x00; + } + } + + + /* + * Insert remaining four octets of FRMR information field: + * +---+---+---+---+---+---+---+---+ + * | X | X | X | X | V(S) | + * +---+---+---+---+---+---+---+---+ + * | V(S) | X | V(R) | + * +---+---+---+---+---+---+---+---+ + * | V(R) |C/R| + * +---+---+---+---+---+---+---+---+ + * | X | X | X | X | W4| W3| W2| W1| + * +---+---+---+---+---+---+---+---+ + */ + sdu_data[octet++] = (UBYTE)(v_s >> 5) & 0x0F; + sdu_data[octet++] = (UBYTE)(v_s << 3) | ((UBYTE)(v_r >> 7) & 0x0003); + sdu_data[octet++] = (UBYTE)(v_r << 1) | (cr_bit EQ SGSN_COMMAND ? + 0x00 : 0x01); + sdu_data[octet++] = reason & 0x0F; + + return; +#else + /* + * Set sdu_data to point to the first free data octet in sdu. sdu_data + * has to be incremented with each octet that is written in sdu to ensure + * that it always points to the first free data octet. + */ + desc3 = (T_desc3*)desc_list3->first; + + help = (U8*)desc3->buffer; + desc_data = &help[desc3->offset + desc3->len]; + + /* + * Adjust length of SDU to include fixed sized FRMR information field. + */ + desc3->len += U_FRMR_INFO_SIZE; + desc_list3->list_len += U_FRMR_INFO_SIZE; + + /* + * Set ctrl to point to the first octet of the faulty frame control field. + */ + ctrl = &frame->sdu.buf[(frame->sdu.o_buf/8)+1]; + + /* + * If ctrl_length is unknown, the frame type is known, therefore the length + * of the control field can be easily determined. + */ + if (ctrl_length EQ FRMR_CTRL_LENGTH_UNKNOWN) + { + if (pdu_type EQ I_FRAME) + { + ctrl_length = I_CTRL_OCTETS; + } + else if (pdu_type EQ S_FRAME) + { + ctrl_length = S_CTRL_OCTETS; + } + else if (pdu_type EQ UI_FRAME) + { + ctrl_length = UI_CTRL_OCTETS; + } + else if (pdu_type EQ U_FRAME) + { + ctrl_length = U_CTRL_OCTETS; + } + else + { + /* + * Undefined control field type, set control field length to SDU length + * minus address field, limited to six octets. + */ + TRACE_ERROR ("undefined control field type"); + ctrl_length = desc3->len - 1 < 6 ? (desc3->len - 1) : 6; + } + } + else /* ctrl_length is already known */ + { + /* + * Limit control field length to six octets, according to 04.64. + */ + ctrl_length = ctrl_length < 6 ? ctrl_length : 6; + } + + + /* + * Copy the first six octets of faulty control field. If the control field + * is smaller that six octets, the unused octets are set to 0. + */ + for (octet = 0; octet < 6; octet++) + { + if (octet < ctrl_length) + { + desc_data[octet] = ctrl[octet]; + } + else + { + desc_data[octet] = 0x00; + } + } + + + /* + * Insert remaining four octets of FRMR information field: + * +---+---+---+---+---+---+---+---+ + * | X | X | X | X | V(S) | + * +---+---+---+---+---+---+---+---+ + * | V(S) | X | V(R) | + * +---+---+---+---+---+---+---+---+ + * | V(R) |C/R| + * +---+---+---+---+---+---+---+---+ + * | X | X | X | X | W4| W3| W2| W1| + * +---+---+---+---+---+---+---+---+ + */ + desc_data[octet++] = (UBYTE)(v_s >> 5) & 0x0F; + desc_data[octet++] = (UBYTE)(v_s << 3) | ((UBYTE)(v_r >> 7) & 0x0003); + desc_data[octet++] = (UBYTE)(v_r << 1) | (cr_bit EQ SGSN_COMMAND ? + 0x00 : 0x01); + desc_data[octet++] = reason & 0x0F; + + return; +#endif +} /* u_insert_frmr_information() */ + + +/* ++------------------------------------------------------------------------------ +| Function : u_send_sabm ++------------------------------------------------------------------------------ +| Description : This procedure allocates a LL_UNITDATA_REQ primitive, fills in +| the required parameters, builds an U frame header containing +| an SABM command, and sends this primitive to TX. _Before_ it is +| sent to TX, T200 has to be started, because otherwise the +| primitive may not be valid anymore. +| +| Parameters : +| ++------------------------------------------------------------------------------ +*/ +GLOBAL void u_send_sabm (void) +{ + USHORT sdu_byte_len = 0; +#ifdef LL_DESC + T_desc3 *desc3; +#endif + + TRACE_FUNCTION( "u_send_sabm" ); + + u_tag_xid_parameters(0, TRUE); + + /* + * Calculate bit length of tagged XID parameters. First, add + * the size of each tagged parameter. The size of layer-3 + * parameters is variable, so add the given length in octets. + */ + ADD_IF_TAGGED (sdu_byte_len, XID_VERSION); + ADD_IF_TAGGED (sdu_byte_len, XID_T200); + ADD_IF_TAGGED (sdu_byte_len, XID_N200); + ADD_IF_TAGGED (sdu_byte_len, XID_N201_U); + ADD_IF_TAGGED (sdu_byte_len, XID_N201_I); + ADD_IF_TAGGED (sdu_byte_len, XID_MD); + ADD_IF_TAGGED (sdu_byte_len, XID_MU); + ADD_IF_TAGGED (sdu_byte_len, XID_KD); + ADD_IF_TAGGED (sdu_byte_len, XID_KU); + if (llc_data->u->xid_tag & (0x00000001L << XID_LAYER_3)) + { + sdu_byte_len += llc_data->requested_l3_xid->length + 2; + } + + /* + * Add SDU header and FCS field to the XID response size to get + * the overall SDU size. + */ + sdu_byte_len += U_HDR_SIZE + FCS_SIZE; + +#ifdef LL_DESC + { + PALLOC(ll_unitdesc_req, LL_UNITDESC_REQ); + + desc3 = llc_palloc_desc((USHORT)sdu_byte_len, 0); /* One desc3 descriptor and buffer allocated */ + + ll_unitdesc_req->sapi = llc_data->current_sapi; + ll_unitdesc_req->tlli = llc_data->u->current_tlli; + /* + * No attach to primitive necessary. For retransmission prim + * data is copied in T200. + */ + ll_unitdesc_req->attached_counter = CCI_NO_ATTACHE; + /* + * LLC does not know the QoS profile. + */ + ll_unitdesc_req->ll_qos = llc_data->cur_qos; + + TRACE_EVENT_P1("peak throughput = %d",llc_data->cur_qos.peak); + + /* + * LLC signalling frames are always sent with highest priority. + */ + ll_unitdesc_req->radio_prio = llc_data->cur_radio_prio; + TRACE_EVENT_P1("radio priority = %d",llc_data->cur_radio_prio); +#ifdef REL99 + /* + * From 24.008 & 23.060 it is interpreted that for all signalling data, a + * predefined PFI LL_PFI_SIGNALING shall be used. + */ + ll_unitdesc_req->pkt_flow_id = llc_data->cur_pfi; + TRACE_EVENT_P1("packet flow id = %d",llc_data->cur_pfi); +#endif /* REL99 */ + /* + * Set parameter of descriptor list3 + */ + ll_unitdesc_req->desc_list3.first = (ULONG)desc3; + ll_unitdesc_req->desc_list3.list_len = desc3->len; + + u_build_u_frame (&ll_unitdesc_req->desc_list3, MS_COMMAND, + llc_data->current_sapi, 1, U_SABM); + + u_insert_xid (&ll_unitdesc_req->desc_list3, MS_COMMAND); + + /* + * T200 has to be started _before_ the primitive is sent to TX, because + * the primitive is copied by t200_start() and it may not be valid + * anymore after sending to TX. + */ + sig_u_t200_start_req (ll_unitdesc_req, GRLC_DTACS_DEF); + + sig_u_tx_data_req (ll_unitdesc_req, GRLC_DTACS_DEF); + } + +#else + { + PALLOC_SDU (ll_unitdata_req, LL_UNITDATA_REQ, (USHORT)(sdu_byte_len*8)); + + ll_unitdata_req->sapi = llc_data->current_sapi; + ll_unitdata_req->tlli = llc_data->u->current_tlli; + /* + * No attach to primitive necessary. For retransmission prim + * data is copied in T200. + */ + ll_unitdata_req->attached_counter = CCI_NO_ATTACHE; + /* + * LLC does not know the QoS profile. + */ + ll_unitdata_req->ll_qos.peak = LL_PEAK_SUB; + /* + * LLC signalling frames are always sent with highest priority. + */ + ll_unitdata_req->radio_prio = LL_RADIO_PRIO_1; +#ifdef REL99 + /* + * From 24.008 & 23.060 it is interpreted that for all signalling data, a + * predefined PFI LL_PFI_SIGNALING shall be used. + */ + ll_unitdata_req->pkt_flow_id = LL_PFI_SIGNALING; +#endif /* REL99 */ + + u_build_u_frame (&ll_unitdata_req->sdu, MS_COMMAND, + llc_data->current_sapi, 1, U_SABM); + + u_insert_xid (&ll_unitdata_req->sdu, MS_COMMAND); + + /* + * T200 has to be started _before_ the primitive is sent to TX, because + * the primitive is copied by t200_start() and it may not be valid + * anymore after sending to TX. + */ + sig_u_t200_start_req (ll_unitdata_req, GRLC_DTACS_DEF); + + sig_u_tx_data_req (ll_unitdata_req, GRLC_DTACS_DEF); + } +#endif + return; +} /* u_send_sabm() */ + + +/* ++------------------------------------------------------------------------------ +| Function : u_send_disc ++------------------------------------------------------------------------------ +| Description : This procedure allocates a LL_UNITDATA_REQ primitive, fills in +| the required parameters, builds an U frame header containing +| an DISC command, and sends this primitive to TX. _Before_ it is +| sent to TX, T200 has to be started, because otherwise the +| primitive may not be valid anymore. +| +| Parameters : +| ++------------------------------------------------------------------------------ +*/ +GLOBAL void u_send_disc (void) +{ +#ifdef LL_DESC + T_desc3* desc3; +#endif + TRACE_FUNCTION( "u_send_disc" ); + + { +#ifndef LL_DESC + PALLOC_SDU (ll_unitdesc_req, LL_UNITDATA_REQ, + U_HDR_SIZE_BITS + FCS_SIZE_BITS); +#else + PALLOC (ll_unitdesc_req, LL_UNITDESC_REQ); + + desc3 = llc_palloc_desc((U_HDR_SIZE_BITS + FCS_SIZE_BITS)/8, 0); + ll_unitdesc_req->desc_list3.first = (ULONG)desc3; + ll_unitdesc_req->desc_list3.list_len = desc3->len; +#endif + + ll_unitdesc_req->sapi = llc_data->current_sapi; + ll_unitdesc_req->tlli = llc_data->u->current_tlli; + /* + * No attach to primitive necessary. For retransmission prim + * data is copied in T200. + */ + ll_unitdesc_req->attached_counter = CCI_NO_ATTACHE; + /* + * LLC does not know the QoS profile. + */ + ll_unitdesc_req->ll_qos = llc_data->cur_qos; + + TRACE_EVENT_P1("peak throughput = %d",llc_data->cur_qos.peak); + + /* + * LLC signalling frames are always sent with highest priority. + */ + ll_unitdesc_req->radio_prio = llc_data->cur_radio_prio; + TRACE_EVENT_P1("radio priority = %d",llc_data->cur_radio_prio); + +#ifdef REL99 + /* + * From 24.008 & 23.060 it is interpreted that for all signalling data, a + * predefined PFI LL_PFI_SIGNALING shall be used. + */ + ll_unitdesc_req->pkt_flow_id = llc_data->cur_pfi; + TRACE_EVENT_P1("packet flow id = %d",llc_data->cur_pfi); + +#endif /* REL99 */ + + + u_build_u_frame + ( +#ifndef LL_DESC + &ll_unitdesc_req->sdu, +#else + &ll_unitdesc_req->desc_list3, +#endif + MS_COMMAND, + llc_data->current_sapi, 1, U_DISC + ); + + /* + * T200 has to be started _before_ the primitive is sent to TX, because + * the primitive is copied by t200_start() and it may not be valid + * anymore after sending to TX. + */ + sig_u_t200_start_req (ll_unitdesc_req, GRLC_DTACS_DEF); + + sig_u_tx_data_req (ll_unitdesc_req, GRLC_DTACS_DEF); + } + + return; +} /* u_send_disc() */ + +#ifdef REL99 +/* ++------------------------------------------------------------------------------ +| Function : sig_tx_u_send_null ++------------------------------------------------------------------------------ +| Description : This procedure allocates a LL_UNITDATA_REQ primitive, fills in +| the required parameters, builds an U frame header containing +| an NULL command, and sends this primitive to TX. +| +| Parameters : cause - frame cause indicates grr about cell notification +| ++------------------------------------------------------------------------------ +*/ +GLOBAL void sig_tx_u_send_null (UBYTE cause) +{ +#ifdef LL_DESC + T_desc3* desc3; +#endif + TRACE_FUNCTION( "sig_tx_u_send_null" ); + + { +#ifndef LL_DESC + PALLOC_SDU (ll_unitdesc_req, LL_UNITDATA_REQ, + U_HDR_SIZE_BITS + FCS_SIZE_BITS); +#else + PALLOC (ll_unitdesc_req, LL_UNITDESC_REQ); + desc3 = llc_palloc_desc((U_HDR_SIZE_BITS + FCS_SIZE_BITS)/8, 0); + ll_unitdesc_req->desc_list3.first = (ULONG)desc3; + ll_unitdesc_req->desc_list3.list_len = desc3->len; +#endif + /* + * Set TLLI for current transaction. + */ + llc_data->u->current_tlli = llc_data->tlli_new; + + ll_unitdesc_req->sapi = llc_data->current_sapi; + ll_unitdesc_req->tlli = llc_data->u->current_tlli; + /* + * No attach to primitive necessary. For retransmission prim + * data is copied in T200. + */ + ll_unitdesc_req->attached_counter = CCI_NO_ATTACHE; + /* + * LLC does not know the QoS profile. + */ + ll_unitdesc_req->ll_qos.peak = GRLC_PEAK_SUB; + /* + * LLC signalling frames are always sent with highest priority. + */ + ll_unitdesc_req->radio_prio = GRLC_RADIO_PRIO_1; + + u_build_u_frame + ( +#ifndef LL_DESC + &ll_unitdesc_req->sdu, +#else + &ll_unitdesc_req->desc_list3, +#endif + MS_COMMAND, + llc_data->current_sapi, 0, U_NULL + ); + + sig_u_tx_data_req (ll_unitdesc_req, cause); + } + return; +} /* sig_tx_u_send_null() */ + +#endif /* REL99 */ +/* ++------------------------------------------------------------------------------ +| Function : u_send_ua ++------------------------------------------------------------------------------ +| Description : This procedure allocates a LL_UNITDATA_REQ primitive, fills in +| the required parameters, builds an U frame header containing +| an UA response with the given pf_bit setting, and sends this +| primitive to TX. The parameter include_xid indicates if an +| XID information field (according to llc_xid_tag) shall be +| included (=TRUE) or not. +| +| Parameters : pf_bit - setting of the P/F bit in the received command +| include_xid - include XID information field in frame, or not +| ++------------------------------------------------------------------------------ +*/ +GLOBAL void u_send_ua (T_BIT pf_bit, + BOOL include_xid) +{ +#ifdef LL_DESC +T_desc3* desc3; +#endif + USHORT byte_len = 0; + + + TRACE_FUNCTION( "u_send_ua" ); + + if (include_xid EQ TRUE) + { + + u_tag_xid_parameters(1, TRUE); + + /* + * Calculate bit length of tagged XID parameters. First, add + * the size of each tagged parameter. The size of layer-3 + * parameters is variable, so add the given length in octets. + */ + ADD_IF_TAGGED (byte_len, XID_VERSION); + ADD_IF_TAGGED (byte_len, XID_T200); + ADD_IF_TAGGED (byte_len, XID_N200); + ADD_IF_TAGGED (byte_len, XID_N201_U); + ADD_IF_TAGGED (byte_len, XID_N201_I); + ADD_IF_TAGGED (byte_len, XID_MD); + ADD_IF_TAGGED (byte_len, XID_MU); + ADD_IF_TAGGED (byte_len, XID_KD); + ADD_IF_TAGGED (byte_len, XID_KU); + if (llc_data->u->xid_tag & (0x00000001L << XID_LAYER_3)) + { + byte_len += llc_data->requested_l3_xid->length + 2; + } + } + + /* + * Add data header and FCS field to the XID response size to get + * the overall data size. + */ + byte_len += U_HDR_SIZE + FCS_SIZE; + + { +#ifndef LL_DESC + PALLOC_SDU (ll_unitdesc_req, LL_UNITDATA_REQ, (USHORT)(byte_len*8)); +#else + PALLOC (ll_unitdesc_req, LL_UNITDESC_REQ); + + desc3 = llc_palloc_desc((USHORT)(byte_len), 0); + ll_unitdesc_req->desc_list3.first = (ULONG)desc3; + ll_unitdesc_req->desc_list3.list_len = desc3->len; + +#endif + + ll_unitdesc_req->sapi = llc_data->current_sapi; + ll_unitdesc_req->tlli = llc_data->u->current_tlli; + /* + * No attach to primitive necessary. There is no retransmission. + */ + ll_unitdesc_req->attached_counter = CCI_NO_ATTACHE; + /* + * LLC does not know the QoS profile. + */ + ll_unitdesc_req->ll_qos = llc_data->cur_qos; + TRACE_EVENT_P1("peak throughput = %d",llc_data->cur_qos.peak); + + /* + * LLC signalling frames are always sent with highest priority. + */ + ll_unitdesc_req->radio_prio = llc_data->cur_radio_prio; + TRACE_EVENT_P1("radio priority = %d",llc_data->cur_radio_prio); + +#ifdef REL99 + /* + * From 24.008 & 23.060 it is interpreted that for all signalling data, a + * predefined PFI LL_PFI_SIGNALING shall be used. + */ + ll_unitdesc_req->pkt_flow_id = llc_data->cur_pfi; + TRACE_EVENT_P1("packet flow id = %d",llc_data->cur_pfi); +#endif /* REL99 */ + + u_build_u_frame + ( +#ifndef LL_DESC + &ll_unitdesc_req->sdu, +#else + &ll_unitdesc_req->desc_list3, +#endif + MS_RESPONSE, llc_data->current_sapi, pf_bit, U_UA + ); + + if (include_xid EQ TRUE) + { + u_insert_xid + ( +#ifndef LL_DESC + &ll_unitdesc_req->sdu, +#else + &ll_unitdesc_req->desc_list3, +#endif + MS_RESPONSE + ); + } + + sig_u_tx_data_req (ll_unitdesc_req, GRLC_DTACS_DEF); + } + + llc_init_requested_xid_sapi(llc_data->current_sapi); + return; +} /* u_send_ua() */ + + +/* ++------------------------------------------------------------------------------ +| Function : u_send_dm ++------------------------------------------------------------------------------ +| Description : This procedure allocates a LL_UNITDATA_REQ primitive, fills in +| all required parameters, builds an U frame header containing a +| DM response with the given pf_bit setting, and sends this +| primitive to TX. +| +| Parameters : pf_bit - setting of the P/F bit in the received command +| ++------------------------------------------------------------------------------ +*/ +GLOBAL void u_send_dm (T_BIT pf_bit) +{ +#ifdef LL_DESC + T_desc3* desc3; +#endif + + TRACE_FUNCTION( "u_send_dm" ); + + { +#ifndef LL_DESC + PALLOC_SDU (ll_unitdesc_req, LL_UNITDATA_REQ, + U_DM_SIZE_BITS + FCS_SIZE_BITS); +#else + PALLOC (ll_unitdesc_req, LL_UNITDESC_REQ); + + desc3 = llc_palloc_desc((U_DM_SIZE_BITS + FCS_SIZE_BITS)/8, 0); + ll_unitdesc_req->desc_list3.first = (ULONG)desc3; + ll_unitdesc_req->desc_list3.list_len = desc3->len; +#endif + + ll_unitdesc_req->sapi = llc_data->current_sapi; + ll_unitdesc_req->tlli = llc_data->u->current_tlli; + /* + * No attach to primitive necessary. There is no retransmission. + */ + ll_unitdesc_req->attached_counter = CCI_NO_ATTACHE; + /* + * LLC does not know the QoS profile. + */ + ll_unitdesc_req->ll_qos = llc_data->cur_qos; + + TRACE_EVENT_P1("peak throughput = %d",llc_data->cur_qos.peak); + + + /* + * LLC signalling frames are always sent with highest priority. + */ + ll_unitdesc_req->radio_prio = llc_data->cur_radio_prio; + TRACE_EVENT_P1("radio priority = %d",llc_data->cur_radio_prio); + +#ifdef REL99 + /* + * From 24.008 & 23.060 it is interpreted that for all signalling data, a + * predefined PFI LL_PFI_SIGNALING shall be used. + */ + ll_unitdesc_req->pkt_flow_id = llc_data->cur_pfi; + TRACE_EVENT_P1("packet flow id = %d",llc_data->cur_pfi); + +#endif /* REL99 */ + + u_build_u_frame + ( +#ifndef LL_DESC + &ll_unitdesc_req->sdu, +#else + &ll_unitdesc_req->desc_list3, +#endif + MS_RESPONSE, + llc_data->current_sapi, pf_bit, U_DM + ); + + sig_u_tx_data_req (ll_unitdesc_req, GRLC_DTACS_DEF); + } + + return; +} /* u_send_dm() */ + + +/* ++------------------------------------------------------------------------------ +| Function : u_send_frmr ++------------------------------------------------------------------------------ +| Description : This procedure allocates a LL_UNITDATA_REQ primitive, fills in +| the required parameters, builds an U frame header containing +| an FRMR response, fills in the FRMR information field based on +| the given frame, vs, vr, the cr_bit setting of frame +| (SGSN_COMMAND/REPONSE), and the reason of the frame rejection +| condition (lower nibble conforms to W4-W1 in FRMR response). +| This primitive is then sent to TX. +| +| Parameters : frame - frame that caused the frame rejection condition +| pdu_type - frame type +| ctrl_length - control field length, if known +| vs - current V(S) +| vr - current V(R) +| cr_bit - setting of C/R bit in frame +| reason - reason of the frame rejection condition (lower +| nibble conforms to W4-W1 in FRMR response) +| ++------------------------------------------------------------------------------ +*/ +GLOBAL void u_send_frmr (T_LL_UNITDATA_IND *frame, + T_PDU_TYPE pdu_type, + USHORT ctrl_length, + T_FRAME_NUM vs, + T_FRAME_NUM vr, + T_BIT cr_bit, + UBYTE reason) +{ +#ifdef LL_DESC + T_desc3* desc3; +#endif + + TRACE_FUNCTION( "u_send_frmr" ); + + { +#ifndef LL_DESC + PALLOC_SDU (ll_unitdesc_req, LL_UNITDATA_REQ, + U_FRMR_SIZE_BITS + FCS_SIZE_BITS); +#else + PALLOC (ll_unitdesc_req, LL_UNITDESC_REQ); + + desc3 = llc_palloc_desc((U_FRMR_SIZE_BITS + FCS_SIZE_BITS)/8, 0); + ll_unitdesc_req->desc_list3.first = (ULONG)desc3; + ll_unitdesc_req->desc_list3.list_len = desc3->len; +#endif + + ll_unitdesc_req->sapi = llc_data->current_sapi; + ll_unitdesc_req->tlli = llc_data->u->current_tlli; + /* + * No attach to primitive necessary. There is no retransmission. + */ + ll_unitdesc_req->attached_counter = CCI_NO_ATTACHE; + /* + * LLC does not know the QoS profile. + */ + ll_unitdesc_req->ll_qos = llc_data->cur_qos; + + TRACE_EVENT_P1("peak throughput = %d",llc_data->cur_qos.peak); + + + /* + * LLC signalling frames are always sent with highest priority. + */ + ll_unitdesc_req->radio_prio = llc_data->cur_radio_prio; + TRACE_EVENT_P1("radio priority = %d",llc_data->cur_radio_prio); + +#ifdef REL99 + /* + * From 24.008 & 23.060 it is interpreted that for all signalling data, a + * predefined PFI LL_PFI_SIGNALING shall be used. + */ + ll_unitdesc_req->pkt_flow_id = llc_data->cur_pfi; + TRACE_EVENT_P1("packet flow id = %d",llc_data->cur_pfi); + +#endif /* REL99 */ + + u_build_u_frame + ( +#ifndef LL_DESC + &ll_unitdesc_req->sdu, +#else + &ll_unitdesc_req->desc_list3, +#endif + MS_RESPONSE, + llc_data->current_sapi, 1, U_FRMR + ); + + u_insert_frmr_information + ( +#ifndef LL_DESC + &ll_unitdesc_req->sdu, +#else + &ll_unitdesc_req->desc_list3, +#endif + frame, pdu_type, + ctrl_length, vs, vr, cr_bit, reason + ); + + sig_u_tx_data_req (ll_unitdesc_req, GRLC_DTACS_DEF); + } + + return; +} /* u_send_frmr() */ + + +/* ++------------------------------------------------------------------------------ +| Function : u_send_llgmm_status_ind ++------------------------------------------------------------------------------ +| Description : This procedure allocates an LLGMM_STATUS_IND primitive, fills +| in the error_cause parameter, and sends this primitive to GMM. +| +| Parameters : error_cause - LLGMM error cause +| ++------------------------------------------------------------------------------ +*/ +#ifdef CC_CONCEPT +GLOBAL void u_send_llgmm_status_ind (USHORT error_cause) +#else +GLOBAL void u_send_llgmm_status_ind (UBYTE error_cause) +#endif +{ + TRACE_FUNCTION( "u_send_llgmm_status_ind" ); + + { + PALLOC (llgmm_status_ind, LLGMM_STATUS_IND); /* T_LLGMM_STATUS_IND */ + +#ifdef LL_2to1 + llgmm_status_ind->ps_cause.ctrl_value = CAUSE_is_from_llc; + llgmm_status_ind->ps_cause.value.llc_cause = error_cause; +#else + llgmm_status_ind->error_cause = error_cause; +#endif + + PSEND (hCommGMM, llgmm_status_ind); + } + + return; +} /* u_send_llgmm_status_ind() */ + + +/* ++------------------------------------------------------------------------------ +| Function : u_send_ll_release_cnf ++------------------------------------------------------------------------------ +| Description : This procedure allocates an LL_RELEASE_CNF primitive, fills in +| tlli and sapi parameters, and sends this primitive to SNDCP. +| +| Parameters : +| ++------------------------------------------------------------------------------ +*/ +GLOBAL void u_send_ll_release_cnf (void) +{ + TRACE_FUNCTION( "u_send_ll_release_cnf" ); + + { + PALLOC (ll_release_cnf, LL_RELEASE_CNF); + + ll_release_cnf->sapi = llc_data->current_sapi; + TRACE_1_OUT_PARA("s:%d", llc_data->current_sapi ); + PSEND (hCommSNDCP, ll_release_cnf); + } + + llc_data->u->release_requested = FALSE; + + return; +} /* u_send_ll_release_cnf() */ + + + +/* ++------------------------------------------------------------------------------ +| Function : u_send_ll_status_ind ++------------------------------------------------------------------------------ +| Description : This procedure allocates an LL_STATUS_IND primitive, fills in +| tlli, sapi and cause parameters and sends this proimitive to L3 +| +| Parameters : cause - LL error cause +| ++------------------------------------------------------------------------------ +*/ +#ifdef CC_CONCEPT +GLOBAL void u_send_ll_status_ind (USHORT cause) +#else +GLOBAL void u_send_ll_status_ind (UBYTE cause) +#endif +{ + TRACE_FUNCTION( "u_send_ll_status_ind" ); + + { + PALLOC (ll_status_ind, LL_STATUS_IND); + + ll_status_ind->sapi = llc_data->current_sapi; +#ifdef LL_2to1 + ll_status_ind->ps_cause.ctrl_value = CAUSE_is_from_llc; + ll_status_ind->ps_cause.value.llc_cause = cause; +#else + ll_status_ind->error_cause = cause; +#endif + + TRACE_1_OUT_PARA("s:%d", llc_data->current_sapi ); + PSEND (hCommSNDCP, ll_status_ind); + } + + return; +} /* u_send_ll_status_ind() */ + + + +/* ++------------------------------------------------------------------------------ +| Function : u_send_ll_xid_cnf ++------------------------------------------------------------------------------ +| Description : This procedure allocates an LL_XID_CNF primitive, fills in +| tlli, sapi and L3 parameter (if being negotiated), and sends +| this primitive to SNDCP +| +| Parameters : +| ++------------------------------------------------------------------------------ +*/ +GLOBAL void u_send_ll_xid_cnf (void) +{ + TRACE_FUNCTION( "u_send_ll_xid_cnf" ); + + if (llc_data->decoded_l3_xid.valid EQ TRUE) + { + PALLOC_SDU (ll_xid_cnf, LL_XID_CNF, (USHORT)(llc_data->decoded_l3_xid.length * 8)); + + ll_xid_cnf->sdu.l_buf = llc_data->decoded_l3_xid.length * 8; + ll_xid_cnf->sdu.o_buf = 0; + + memcpy (ll_xid_cnf->sdu.buf, + llc_data->decoded_l3_xid.value, + llc_data->decoded_l3_xid.length); + + ll_xid_cnf->sapi = llc_data->current_sapi; + + ll_xid_cnf->n201_u = *(llc_data->n201_u); + ll_xid_cnf->n201_i = *(llc_data->n201_i); + + TRACE_2_OUT_PARA("s:%d xid-len:%d", llc_data->current_sapi, llc_data->decoded_l3_xid.length ); + PSEND (hCommSNDCP, ll_xid_cnf); + } + else + { + PALLOC_SDU (ll_xid_cnf, LL_XID_CNF, 0); + + ll_xid_cnf->sdu.l_buf = 0; + ll_xid_cnf->sdu.o_buf = 0; + + ll_xid_cnf->sapi = llc_data->current_sapi; + + ll_xid_cnf->n201_u = *(llc_data->n201_u); + ll_xid_cnf->n201_i = *(llc_data->n201_i); + + TRACE_2_OUT_PARA("s:%d xid-len:%d", llc_data->current_sapi, 0 ); + PSEND (hCommSNDCP, ll_xid_cnf); + } + + return; +} /* u_send_ll_xid_cnf() */ + + +/* ++------------------------------------------------------------------------------ +| Function : u_send_ll_establish_ind ++------------------------------------------------------------------------------ +| Description : This procedure allocates an LL_ESTABLISH_IND primitive, fills +| in all necessary parameters using global LLC values (or in case +| of L-3 XID parameters, using the parameters of received XID +| cmd/res), and sends this primitive to SNDCP +| +| Parameters : +| ++------------------------------------------------------------------------------ +*/ +GLOBAL void u_send_ll_establish_ind (void) +{ + USHORT len; + + TRACE_FUNCTION( "u_send_ll_establish_ind" ); + + if (llc_data->decoded_l3_xid.valid EQ TRUE) + { + len = (llc_data->decoded_l3_xid.length * 8); + } + else + { + len = 0; + } + + { + PALLOC_SDU (ll_establish_ind, LL_ESTABLISH_IND, len); + + if (llc_data->decoded_l3_xid.valid EQ TRUE) + { + ll_establish_ind->xid_valid = LL_XID_VALID; + + ll_establish_ind->sdu.o_buf = 0; + ll_establish_ind->sdu.l_buf = llc_data->decoded_l3_xid.length * 8; + + memcpy (ll_establish_ind->sdu.buf, + llc_data->decoded_l3_xid.value, + llc_data->decoded_l3_xid.length); + } + else + { + ll_establish_ind->xid_valid = LL_XID_INVALID; + } + + ll_establish_ind->sapi = llc_data->current_sapi; + + /* + * evaluate N201_U + */ + if (llc_data->u->requested_xid.n201_u.valid AND + llc_data->decoded_xid.n201_u.valid AND + !llc_xid_value_acceptable (llc_data->current_sapi, XID_N201_U, + llc_data->decoded_xid.n201_u.value)) + { + ll_establish_ind->n201_u = llc_data->u->requested_xid.n201_u.value; + } + else if (llc_data->u->requested_xid.n201_u.valid AND + !llc_data->decoded_xid.n201_u.valid AND + !llc_xid_value_acceptable (llc_data->current_sapi, XID_N201_U, + *(llc_data->n201_u))) + { + ll_establish_ind->n201_u = llc_data->u->requested_xid.n201_u.value; + } + else + { + ll_establish_ind->n201_u = *(llc_data->n201_u); + } + + /* + * evaluate N201_I + */ + if (llc_data->u->requested_xid.n201_i.valid AND + llc_data->decoded_xid.n201_i.valid AND + !llc_xid_value_acceptable (llc_data->current_sapi, XID_N201_I, + llc_data->decoded_xid.n201_i.value)) + { + ll_establish_ind->n201_i = llc_data->u->requested_xid.n201_i.value; + } + else if (llc_data->u->requested_xid.n201_i.valid AND + !llc_data->decoded_xid.n201_i.valid AND + !llc_xid_value_acceptable (llc_data->current_sapi, XID_N201_I, + *(llc_data->n201_i))) + { + ll_establish_ind->n201_i = llc_data->u->requested_xid.n201_i.value; + } + else + { + ll_establish_ind->n201_i = *(llc_data->n201_i); + } + + + TRACE_2_OUT_PARA("s:%d xid-len:%d", ll_establish_ind->sapi, len/8); + PSEND (hCommSNDCP, ll_establish_ind); + + return; + } +} /* u_send_ll_establish_ind() */ + + + +/* ++------------------------------------------------------------------------------ +| Function : u_send_ll_establish_cnf ++------------------------------------------------------------------------------ +| Description : This procedure allocates an LL_ESTABLISH_CNF primitive, fills +| in all necessary parameters using global LLC values (or in case +| of L-3 XID parameters, using the parameters of received XID +| cmd/res), and sends this primitive to SNDCP +| +| Parameters : +| ++------------------------------------------------------------------------------ +*/ +GLOBAL void u_send_ll_establish_cnf (void) +{ + TRACE_FUNCTION( "u_send_ll_establish_cnf" ); + + if (llc_data->decoded_l3_xid.valid EQ TRUE) + { + PALLOC_SDU (ll_establish_cnf, LL_ESTABLISH_CNF, + (USHORT)(llc_data->decoded_l3_xid.length * 8)); + + ll_establish_cnf->xid_valid = LL_XID_VALID; + + ll_establish_cnf->sdu.o_buf = 0; + ll_establish_cnf->sdu.l_buf = llc_data->decoded_l3_xid.length * 8; + + memcpy (ll_establish_cnf->sdu.buf, + llc_data->decoded_l3_xid.value, + llc_data->decoded_l3_xid.length); + + ll_establish_cnf->sapi = llc_data->current_sapi; + + ll_establish_cnf->n201_u = *(llc_data->n201_u); + ll_establish_cnf->n201_i = *(llc_data->n201_i); + + TRACE_2_OUT_PARA("s:%d xid-len:%d", llc_data->current_sapi, llc_data->decoded_l3_xid.length ); + PSEND (hCommSNDCP, ll_establish_cnf); + } + else + { + PALLOC_SDU (ll_establish_cnf, LL_ESTABLISH_CNF, 0); + + ll_establish_cnf->xid_valid = LL_XID_INVALID; + + ll_establish_cnf->sapi = llc_data->current_sapi; + + ll_establish_cnf->n201_u = *(llc_data->n201_u); + ll_establish_cnf->n201_i = *(llc_data->n201_i); + + TRACE_2_OUT_PARA("s:%d xid-len:%d", llc_data->current_sapi, 0 ); + PSEND (hCommSNDCP, ll_establish_cnf); + } + + return; +} /* u_send_ll_establish_cnf() */ + + +/* ++------------------------------------------------------------------------------ +| Function : u_send_ll_release_ind ++------------------------------------------------------------------------------ +| Description : This procedure allocates an LL_RELEASE_IND primitive, fills +| in tlli, sapi and cause parameters, and sends this primitive +| to SNDCP +| +| Parameters : cause - LL error cause +| ++------------------------------------------------------------------------------ +*/ +#ifdef CC_CONCEPT +GLOBAL void u_send_ll_release_ind (USHORT cause) +#else +GLOBAL void u_send_ll_release_ind (UBYTE cause) +#endif +{ + TRACE_FUNCTION( "u_send_ll_release_ind" ); + + { + PALLOC (ll_release_ind, LL_RELEASE_IND); + + ll_release_ind->sapi = llc_data->current_sapi; +#ifdef LL_2to1 + ll_release_ind->ps_cause.ctrl_value = CAUSE_is_from_llc; + ll_release_ind->ps_cause.value.llc_cause = cause; +#else + ll_release_ind->cause = cause; +#endif + + TRACE_1_OUT_PARA("s:%d", llc_data->current_sapi ); + PSEND (hCommSNDCP, ll_release_ind); + } + + llc_data->u->release_requested = FALSE; + + return; +} /* u_send_ll_release_ind() */ + + + +/* ++------------------------------------------------------------------------------ +| Function : u_send_ll_xid_ind ++------------------------------------------------------------------------------ +| Description : This procedure allocates an LL_XID_IND primitive, fills +| in all necessary parameters using global LLC values (or in case +| of L-3 XID parameters, using the parameters of received XID +| cmd/res), and sends this primitive to SNDCP +| +| Parameters : +| ++------------------------------------------------------------------------------ +*/ +GLOBAL void u_send_ll_xid_ind (void) +{ + TRACE_FUNCTION( "u_send_ll_xid_ind" ); + + + + if (llc_data->decoded_l3_xid.valid EQ TRUE) + { + PALLOC_SDU (ll_xid_ind, LL_XID_IND, (USHORT)(llc_data->decoded_l3_xid.length * 8)); + + llc_data->u->ll_xid_resp_pending = TRUE; + ll_xid_ind->xid_valid = LL_XID_VALID; + + ll_xid_ind->sdu.o_buf = 0; + ll_xid_ind->sdu.l_buf = llc_data->decoded_l3_xid.length * 8; + + memcpy (ll_xid_ind->sdu.buf, + llc_data->decoded_l3_xid.value, + llc_data->decoded_l3_xid.length); + + ll_xid_ind->sapi = llc_data->current_sapi; + + if (llc_data->u->requested_xid.n201_u.valid AND + !llc_xid_value_acceptable (llc_data->current_sapi, XID_N201_U, + llc_data->decoded_xid.n201_u.value)) + { + ll_xid_ind->n201_u = llc_data->u->requested_xid.n201_u.value; + } + else + { + ll_xid_ind->n201_u = *(llc_data->n201_u); + } + + switch (ll_xid_ind->sapi) + { + case LL_SAPI_3: + case LL_SAPI_5: + case LL_SAPI_9: + case LL_SAPI_11: + if (llc_data->u->requested_xid.n201_i.valid AND + !llc_xid_value_acceptable (llc_data->current_sapi, XID_N201_I, + llc_data->decoded_xid.n201_i.value)) + { + ll_xid_ind->n201_i = llc_data->u->requested_xid.n201_i.value; + } + else + { + ll_xid_ind->n201_i = *(llc_data->n201_i); + } + break; + + default: + ll_xid_ind->n201_i = 0; + break; + } + + /* + * Send primitive LL_XID_IND to either GMM, SNDCP, or GSMS, + * depending on SAPI. + */ + switch (ll_xid_ind->sapi) + { + case LL_SAPI_1: + TRACE_PRIM_TO("GMM"); + TRACE_2_OUT_PARA("s:%d xid-len:%d", ll_xid_ind->sapi, llc_data->decoded_l3_xid.length); + PSEND (hCommGMM, ll_xid_ind); + break; + case LL_SAPI_3: + case LL_SAPI_5: + case LL_SAPI_9: + case LL_SAPI_11: + TRACE_2_OUT_PARA("s:%d xid-len:%d", ll_xid_ind->sapi, llc_data->decoded_l3_xid.length); + PSEND (hCommSNDCP, ll_xid_ind); + break; + case LL_SAPI_7: +#ifdef LL_2to1 + TRACE_PRIM_TO("MM"); + TRACE_2_OUT_PARA("s:%d xid-len:%d", ll_xid_ind->sapi, llc_data->decoded_l3_xid.length); + PSEND (hCommMM, ll_xid_ind); +#else + TRACE_PRIM_TO("GSMS"); + TRACE_2_OUT_PARA("s:%d xid-len:%d", ll_xid_ind->sapi, llc_data->decoded_l3_xid.length); + PSEND (hCommGSMS, ll_xid_ind); +#endif + break; + default: + PFREE (ll_xid_ind); + TRACE_ERROR ("Invalid global SAPI value"); + break; + } + } + else + { + PALLOC_SDU (ll_xid_ind, LL_XID_IND, 0); + + llc_data->u->ll_xid_resp_pending = FALSE; + + ll_xid_ind->xid_valid = LL_XID_INVALID; + + ll_xid_ind->sapi = llc_data->current_sapi; + + ll_xid_ind->n201_u = *(llc_data->n201_u); + + switch (ll_xid_ind->sapi) + { + case LL_SAPI_3: + case LL_SAPI_5: + case LL_SAPI_9: + case LL_SAPI_11: + ll_xid_ind->n201_i = *(llc_data->n201_i); + break; + + default: + ll_xid_ind->n201_i = 0; + break; + } + + /* + * Send primitive LL_XID_IND to either GMM, SNDCP, or GSMS, + * depending on SAPI. + */ + switch (ll_xid_ind->sapi) + { + case LL_SAPI_1: + TRACE_PRIM_TO("GMM"); + TRACE_1_OUT_PARA("s:%d no l3-xid", ll_xid_ind->sapi); + PSEND (hCommGMM, ll_xid_ind); + break; + case LL_SAPI_3: + case LL_SAPI_5: + case LL_SAPI_9: + case LL_SAPI_11: + TRACE_1_OUT_PARA("s:%d no l3-xid", ll_xid_ind->sapi); + PSEND (hCommSNDCP, ll_xid_ind); + break; + case LL_SAPI_7: +#ifdef LL_2to1 + TRACE_PRIM_TO("MM"); + TRACE_1_OUT_PARA("s:%d no l3-xid", ll_xid_ind->sapi); + PSEND (hCommMM, ll_xid_ind); +#else + TRACE_PRIM_TO("GSMS"); + TRACE_1_OUT_PARA("s:%d no l3-xid", ll_xid_ind->sapi); + PSEND (hCommGSMS, ll_xid_ind); +#endif + break; + default: + PFREE (ll_xid_ind); + TRACE_ERROR ("invalid global SAPI value"); + break; + } + } + + return; +} /* u_send_ll_xid_ind() */ + + +/* ++------------------------------------------------------------------------------ +| Function : u_tag_xid_parameters ++------------------------------------------------------------------------------ +| Description : This procedure tags requested parameters +| +| +| Parameters : +| - send_ack_para- used to include/ignore the ack para in XID-FRAME +| ++------------------------------------------------------------------------------ +*/ +GLOBAL void u_tag_xid_parameters (T_BIT cr_bit, BOOL send_ack_para) +{ + TRACE_FUNCTION( "u_tag_xid_parameters" ); + + if (cr_bit EQ MS_RESPONSE) + { + /* + * Do some special handling in case we want to set mD or mU to zero + * (= disable buffered bytes counting) and the peer does not. + */ + if (llc_data->decoded_xid.mu.valid) + { + u_handle_mX_zero_rsp ( llc_data->decoded_xid.mu.value, + llc_data->n201_i, + *(llc_data->mu), XID_MU_MAX, + llc_data->ku, XID_KU_MIN, XID_KU ); + } + + if (llc_data->decoded_xid.md.valid) + { + u_handle_mX_zero_rsp ( llc_data->decoded_xid.md.value, + llc_data->n201_i, + *(llc_data->md), XID_MD_MAX, + llc_data->kd, XID_KD_MIN, XID_KD ); + } + } + + /* + * Tag all possible requested XID parameter + */ + if (cr_bit EQ MS_COMMAND) + { + /* + * In case we are sending a command, we can include all + * parameters, as long they are valid. + */ + TAG_IF_REQUESTED (u->requested_xid.version. , XID_VERSION); + TAG_IF_REQUESTED (u->requested_xid.t200. , XID_T200); + TAG_IF_REQUESTED (u->requested_xid.n200. , XID_N200); + TAG_IF_REQUESTED (u->requested_xid.n201_u. , XID_N201_U); + TAG_IF_REQUESTED (requested_l3_xid-> , XID_LAYER_3); + if(send_ack_para) + { + TRACE_EVENT("COMMAND: ACK MODE PARAMETERS INCLUDED"); + TAG_IF_REQUESTED (u->requested_xid.n201_i. , XID_N201_I); + TAG_IF_REQUESTED (u->requested_xid.md. , XID_MD); + TAG_IF_REQUESTED (u->requested_xid.mu. , XID_MU); + TAG_IF_REQUESTED (u->requested_xid.kd. , XID_KD); + TAG_IF_REQUESTED (u->requested_xid.ku. , XID_KU); + } + else + { + llc_data->u->requested_xid.n201_i.valid = FALSE; + llc_data->u->requested_xid.md.valid = FALSE; + llc_data->u->requested_xid.mu.valid = FALSE; + llc_data->u->requested_xid.kd.valid = FALSE; + llc_data->u->requested_xid.ku.valid = FALSE; + } + + } + else + { + /* + * In case we are sending a response, we can include all valid + * parameters which are in line with the sense of negotiation. + */ + TAG_IF_REQUESTED_RSP (XID_SENSE_UP, n200, XID_N200); + TAG_IF_REQUESTED_RSP (XID_SENSE_DOWN, n201_u, XID_N201_U); + + if(send_ack_para) + { + TRACE_EVENT("RESPONSE: ACK MODE PARAMETERS INCLUDED"); + TAG_IF_REQUESTED_RSP (XID_SENSE_DOWN, n201_i, XID_N201_I); + TAG_IF_REQUESTED_RSP (XID_SENSE_DOWN, md, XID_MD); + TAG_IF_REQUESTED_RSP (XID_SENSE_DOWN, mu, XID_MU); + TAG_IF_REQUESTED_RSP (XID_SENSE_DOWN, kd, XID_KD); + TAG_IF_REQUESTED_RSP (XID_SENSE_DOWN, ku, XID_KU); + } + + if (llc_data->u->requested_xid.t200.valid) + { + if (llc_data->decoded_xid.t200.valid) + { + /* simple add parameter. Sense of negotiation is already checked */ + llc_data->u->xid_tag |= (0x00000001L << XID_T200); + } + else + { + /* Sense of negotiation compared with current values */ + if (llc_data->u->requested_xid.t200.value XID_SENSE_UP INT2XID(llc_data->t200->length)) + { + llc_data->u->xid_tag |= (0x00000001L << XID_T200); + } + } + } + /* + * L3 parameter cannot be included in response, if not included in + * request + */ + + /* + * If an XID parameter which was not included in the SGSN command, + * but included in the MS Response, must be included in every Response + * until the parameter is explicitly negotiated by the SGSN + */ + + if(llc_data->u->xid_tag_negotiate) + { + llc_data->u->xid_tag |= llc_data->u->xid_tag_negotiate; + } + } +} /* u_tag_xid_parameters() */ + +/* ++------------------------------------------------------------------------------ +| Function : u_send_xid ++------------------------------------------------------------------------------ +| Description : This procedure allocates a LL_UNITDATA_REQ primitive, fills in +| the required parameters, builds an U frame header containing an +| XID command/response (depending on the cr_bit setting), inserts +| all XID parmameters that are to be negotiated, and sends this +| primitive to TX. If a command frame is being sent (see cr_bit), +| T200 has to be started _before_ the primitive is sent to TX, +| because otherwise the primitive may not be valid anymore. +| +| NOTE: T200 must not be running, when an XID command is to be +| sent. This must be ensured by the caller of u_send_xid(). +| +| Parameters : +| ++------------------------------------------------------------------------------ +*/ +GLOBAL void u_send_xid (T_BIT cr_bit) +{ +#ifdef LL_DESC + T_desc3* desc3; +#endif + USHORT byte_len = 0; + + TRACE_FUNCTION( "u_send_xid" ); + + /* + * Calculate bit length of tagged XID parameters. First, add + * the size of each tagged parameter. The size of layer-3 + * parameters is variable, so add the given length in octets. + */ + ADD_IF_TAGGED (byte_len, XID_VERSION); + ADD_IF_TAGGED (byte_len, XID_T200); + ADD_IF_TAGGED (byte_len, XID_N200); + ADD_IF_TAGGED (byte_len, XID_N201_U); + /*Acknowledge Mode Parameters will be ignored in XID FRAME: + ADD_IF_TAGGED (byte_len, XID_N201_I); + ADD_IF_TAGGED (byte_len, XID_MD); + ADD_IF_TAGGED (byte_len, XID_MU); + ADD_IF_TAGGED (byte_len, XID_KD); + ADD_IF_TAGGED (byte_len, XID_KU); + */ + if (llc_data->u->xid_tag & (0x00000001L << XID_LAYER_3)) + { + byte_len += llc_data->requested_l3_xid->length + 2; + } + + /* + * Add data header and FCS field to the XID response size to get + * the overall data size. + */ + byte_len += U_HDR_SIZE + FCS_SIZE; + + { +#ifndef LL_DESC + PALLOC_SDU (ll_unitdesc_req, LL_UNITDATA_REQ, (USHORT)(byte_len*8)); +#else + PALLOC (ll_unitdesc_req, LL_UNITDESC_REQ); + + desc3 = llc_palloc_desc(byte_len, 0); + + ll_unitdesc_req->desc_list3.first = (ULONG)desc3; + ll_unitdesc_req->desc_list3.list_len = desc3->len; +#endif + ll_unitdesc_req->sapi = llc_data->current_sapi; + ll_unitdesc_req->tlli = llc_data->tlli_new; + /* + * LLC does not know the QoS profile. + */ + ll_unitdesc_req->ll_qos = llc_data->cur_qos; + TRACE_EVENT_P1("peak throughput = %d",llc_data->cur_qos.peak); + + /* + * LLC signalling frames are always sent with highest priority. + */ + ll_unitdesc_req->radio_prio = llc_data->cur_radio_prio; + TRACE_EVENT_P1("radio priority = %d",llc_data->cur_radio_prio); + +#ifdef REL99 + /* + * From 24.008 & 23.060 it is interpreted that for all signalling data, a + * predefined PFI LL_PFI_SIGNALING shall be used. + */ + ll_unitdesc_req->pkt_flow_id = llc_data->cur_pfi; + TRACE_EVENT_P1("packet flow id = %d",llc_data->cur_pfi); + +#endif /* REL99 */ + /* + * No attach to primitive necessary. For retransmission prim + * data is copied in T200 (if requested). + */ + ll_unitdesc_req->attached_counter = CCI_NO_ATTACHE; + + /* + * Label U_SEND_XID_CONT + */ + + /* + * pf_bit must always be 1 when XID cmd/res are being sent + */ + u_build_u_frame + ( +#ifndef LL_DESC + &ll_unitdesc_req->sdu, +#else + &ll_unitdesc_req->desc_list3, +#endif + cr_bit, llc_data->current_sapi, 1, U_XID + ); + + u_insert_xid + ( +#ifndef LL_DESC + &ll_unitdesc_req->sdu, +#else + &ll_unitdesc_req->desc_list3, +#endif + cr_bit + ); + + if (cr_bit EQ MS_COMMAND) + { + /* + * remember that an XID command is currently being sent + */ + llc_data->u->xid_pending = TRUE; + + /* + * T200 has to be started !before! the primitive is sent to TX. + */ + sig_u_t200_start_req (ll_unitdesc_req, GRLC_DTACS_DEF); + } + + /* + * U frames are always sent with default cause + */ + sig_u_tx_data_req (ll_unitdesc_req, GRLC_DTACS_DEF); + } + + if (cr_bit EQ MS_RESPONSE) + { + llc_init_requested_xid_sapi(llc_data->current_sapi); + } + + return; +} /* u_send_xid() */ + + + +/* ++------------------------------------------------------------------------------ +| Function : u_handle_optimization ++------------------------------------------------------------------------------ +| Description : This procedure adds the values of the requested XID parameter +| to the decoded XID structure, if they are not included (as an +| optimization issue). +| +| Parameters : +| ++------------------------------------------------------------------------------ +*/ +GLOBAL void u_handle_optimization (void) +{ + + TRACE_FUNCTION ("u_handle_optimization"); + + + if (llc_data->u->xid_tag_sent & (0x00000001L << XID_VERSION) AND + llc_data->decoded_xid.version.valid == FALSE ) + { + llc_data->decoded_xid.version.valid = TRUE; + llc_data->decoded_xid.version.value = llc_data->u->requested_xid.version.valid + ? llc_data->u->requested_xid.version.value + : llc_data->version; + } + + if (llc_data->u->xid_tag_sent & (0x00000001L << XID_T200) AND + llc_data->decoded_xid.t200.valid == FALSE ) + { + llc_data->decoded_xid.t200.valid = TRUE; + llc_data->decoded_xid.t200.value = llc_data->u->requested_xid.t200.valid + ? llc_data->u->requested_xid.t200.value + : ((USHORT)INT2XID (llc_data->t200->length)); + } + + if (llc_data->u->xid_tag_sent & (0x00000001L << XID_N200) AND + llc_data->decoded_xid.n200.valid == FALSE ) + { + llc_data->decoded_xid.n200.valid = TRUE; + llc_data->decoded_xid.n200.value = llc_data->u->requested_xid.n200.valid + ? llc_data->u->requested_xid.n200.value + : *(llc_data->n200); + } + + if (llc_data->u->xid_tag_sent & (0x00000001L << XID_N201_U) AND + llc_data->decoded_xid.n201_u.valid == FALSE ) + { + llc_data->decoded_xid.n201_u.valid = TRUE; + llc_data->decoded_xid.n201_u.value = llc_data->u->requested_xid.n201_u.valid + ? llc_data->u->requested_xid.n201_u.value + : *(llc_data->n201_u); + } + + if (llc_data->u->xid_tag_sent & (0x00000001L << XID_N201_I) AND + llc_data->decoded_xid.n201_i.valid == FALSE ) + { + llc_data->decoded_xid.n201_i.valid = TRUE; + llc_data->decoded_xid.n201_i.value = llc_data->u->requested_xid.n201_i.valid + ? llc_data->u->requested_xid.n201_i.value + : *(llc_data->n201_i); + } + + if (llc_data->u->xid_tag_sent & (0x00000001L << XID_MD) AND + llc_data->decoded_xid.md.valid == FALSE ) + { + llc_data->decoded_xid.md.valid = TRUE; + llc_data->decoded_xid.md.value = llc_data->u->requested_xid.md.valid + ? llc_data->u->requested_xid.md.value + : *(llc_data->md); + } + + if (llc_data->u->xid_tag_sent & (0x00000001L << XID_MU) AND + llc_data->decoded_xid.mu.valid == FALSE ) + { + llc_data->decoded_xid.mu.valid = TRUE; + llc_data->decoded_xid.mu.value = llc_data->u->requested_xid.mu.valid + ? llc_data->u->requested_xid.mu.value + : *(llc_data->mu); + } + + if (llc_data->u->xid_tag_sent & (0x00000001L << XID_KD) AND + llc_data->decoded_xid.kd.valid == FALSE ) + { + llc_data->decoded_xid.kd.valid = TRUE; + llc_data->decoded_xid.kd.value = llc_data->u->requested_xid.kd.valid + ? llc_data->u->requested_xid.kd.value + : *(llc_data->kd); + } + + if (llc_data->u->xid_tag_sent & (0x00000001L << XID_KU) AND + llc_data->decoded_xid.ku.valid == FALSE ) + { + llc_data->decoded_xid.ku.valid = TRUE; + llc_data->decoded_xid.ku.value = llc_data->u->requested_xid.ku.valid + ? llc_data->u->requested_xid.ku.value + : *(llc_data->ku); + } + + + /* + * Layer 3 XID must be included in resonse, if it was included in request + */ + +} /* u_handle_optimization() */ + +