view src/g23m-gprs/llc/llc_uf.c @ 268:f2e52cab0a73

abb_inth.c: check all interrupt causes, not just one The original code used if - else if - else if etc constructs, thus the first detected interrupt was the only one handled. However, Iota ITSTATREG is a clear-on-read register, thus if we only handle the first detected interrupt and skip checking the others, then the other interrupts will be lost, if more than one interrupt happened to occur in one ABB interrupt handling cycle - a form of rare race condition. Change the code to check all interrupts that were read in this cycle.
author Mychaela Falconia <falcon@freecalypso.org>
date Sun, 13 Jun 2021 18:17:53 +0000
parents fa8dc04885d8
children
line wrap: on
line source

  /*
+-----------------------------------------------------------------------------
|  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() */