view src/g23m-gprs/llc/llc_us.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
|             functions to handles the incoming process internal signals as
|             described in the SDL-documentation (U-statemachine)
+-----------------------------------------------------------------------------
*/

#ifndef LLC_US_C
#define LLC_US_C
#endif

#define ENTITY_LLC

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

#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"

#include "llc_uf.h"     /* to get local U functions */
#include "llc_ul.h"     /* to get local U labels */
#include "llc_t200s.h"  /* to get signal interface to T200 */
#include "llc_txs.h"    /* to get signal interface to TX */
#include "llc_llmes.h"  /* to get signal interface to LLME */

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

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

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

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



/*
+------------------------------------------------------------------------------
| Function    : sig_llme_u_assign_req
+------------------------------------------------------------------------------
| Description : Handles the internal signal SIG_LLME_U_ASSIGN_REQ
|
| Parameters  :
|
+------------------------------------------------------------------------------
*/
GLOBAL void sig_llme_u_assign_req (void)
{
  TRACE_ISIG( "sig_llme_u_assign_req" );

  switch (GET_STATE(U))
  {
    case U_TLLI_UNASSIGNED:
      u_init_sapi();
      SET_STATE (U, U_ADM);
      break;
    default:
      TRACE_ERROR( "SIG_LLME_U_ASSIGN_REQ unexpected" );
      break;
  }
} /* sig_llme_u_assign_req() */



/*
+------------------------------------------------------------------------------
| Function    : sig_llme_u_unassign_req
+------------------------------------------------------------------------------
| Description : Handles the internal signal SIG_LLME_U_UNASSIGN_REQ
|
| Parameters  :
|
+------------------------------------------------------------------------------
*/
GLOBAL void sig_llme_u_unassign_req (void)
{
  TRACE_ISIG( "sig_llme_u_unassign_req" );

  switch (GET_STATE(U))
  {
    case U_TLLI_UNASSIGNED:
      TRACE_ERROR( "SIG_LLME_U_UNASSIGN_REQ unexpected" );
      break;

    default:
      sig_u_t200_stop_req();
      SET_STATE (U, U_TLLI_UNASSIGNED);
      break;
  }
} /* sig_llme_u_unassign_req() */



/*
+------------------------------------------------------------------------------
| Function    : sig_rx_u_data_ind
+------------------------------------------------------------------------------
| Description : Handles the internal signal SIG_RX_U_DATA_IND
|
| Parameters  : ll_unitdata_ind - a valid pointer to an LL-UNITDATA-IND
|                                 primitive
|               command - contains the command within the primitive
|               cr_bit - setting of the C/R bit
|               pf_bit - setting of the P/F bit
|
+------------------------------------------------------------------------------
*/
GLOBAL void sig_rx_u_data_ind (T_LL_UNITDATA_IND *ll_unitdata_ind,
                               T_COMMAND command,
                               T_BIT cr_bit,
                               T_BIT pf_bit)
{
  BOOL              reset_received;
  BOOL              xid_ind;
  BOOL              rc_xid_valid;
  BOOL              l3p_outstanding;


  TRACE_ISIG( "sig_rx_u_data_ind" );

  /*
   * Set TLLI for current transaction.
   */
  llc_data->u->current_tlli = ll_unitdata_ind->tlli;

  switch (GET_STATE(U))
  {
    case U_ADM:
      if (cr_bit EQ SGSN_COMMAND)
      {
        /*
         * Label ADM_COMMAND
         */
        switch (command)
        {
          case U_SABM: /* COMMAND ADM */
            /*
             * Label ADM_SABM
             */
            if (pf_bit EQ 1)
            {
              if ( (llc_data->current_sapi EQ LL_SAPI_1) ||
                   (llc_data->current_sapi EQ LL_SAPI_7)  )
              {
                /*
                 * ABM not possible on SAPI 1 and 7
                 */
                PFREE (ll_unitdata_ind);

                u_send_dm (pf_bit);
              }
              else /* current_sapi is one of 3, 5, 9, or 11 */
              {
                rc_xid_valid = u_check_xid (&(ll_unitdata_ind->sdu),
                  cr_bit, command);

                PFREE (ll_unitdata_ind);

                if (rc_xid_valid)
                {
                  /*
                   * No check needed, reset_received cannot be TRUE
                   * (<R.LLC.XID_INVA.A.003>) and xid_ind is meaningless
                   * (LL_ESTABLISH_IND contains N201-U/I).
                   */
                  u_eval_xid (cr_bit, &reset_received, &xid_ind);

                  /*
                   * Check XID/SABM collision issue - XID command shall
                   * be treated as not transmitted.
                   */
                  if (llc_data->u->xid_pending)
                  {
                    llc_data->u->xid_pending = FALSE;

                    sig_u_t200_stop_req();
                  }

                  /*
                   * Label S_EST_IND
                   */
                  u_label_s_est_ind (pf_bit);
                }
              }
            }
            else /* pf_bit is not set */
            {
              PFREE (ll_unitdata_ind);
            }
            break;

          case U_XID: /* COMMAND  ADM */
            /*
             * Label XID
             */
            u_label_xid (ll_unitdata_ind, command, cr_bit, pf_bit);
            break;

          case U_DISC: /* COMMAND ADM */
            PFREE (ll_unitdata_ind);
            if (pf_bit EQ 1)
            {
              u_send_dm (pf_bit);
            }
            break;

          default:
            if (pf_bit EQ 1)
            {
              llc_data->u->frmr_reason = FRMR_UNDEFINED_CTRL;
              /*
               * Label FRMR_COND
               */
              u_label_frmr_cond (ll_unitdata_ind, cr_bit,
                llc_data->u->frmr_reason);
            }
            else
            {
              PFREE (ll_unitdata_ind);
            }
            break;
        }
      }
      else /* SGSN_RESPONSE */
      {
        /*
         * Label ADM_RESPONSE
         */
        switch (command)
        {
          case U_UA: /* RESPONSE ADM */
            PFREE (ll_unitdata_ind);
            u_send_llgmm_status_ind (LLGMM_ERRCS_MULT_ASS_TLLI);
            break;

          case U_DM: /* RESPONSE ADM */
            PFREE (ll_unitdata_ind);
            break;

          case U_XID: /* RESPONSE ADM */
           /*
            * Label XIDRES
            */
            u_label_xidres (ll_unitdata_ind, command, cr_bit, pf_bit);
            break;

          case U_FRMR: /* RESPONSE ADM */
            PFREE (ll_unitdata_ind);
            u_send_llgmm_status_ind (LLGMM_ERRCS_FRMR_RECEIVED);
            break;

          default:
            llc_data->u->frmr_reason = FRMR_UNDEFINED_CTRL;
            /*
             * Label FRMR_COND
             */
            u_label_frmr_cond (ll_unitdata_ind, cr_bit,
              llc_data->u->frmr_reason);
            break;
        }
      }
      break; /* U_ADM */

    case U_LOCAL_ESTABLISHMENT:
      if (cr_bit EQ SGSN_COMMAND)
      {
        /*
         * Label LEST_COMMAND
         */
        switch (command)
        {
          case U_SABM: /* COMMAND LOCAL_ESTABLISHMENT */
            /*
             * Label LEST_SABM
             */

            if (pf_bit EQ 1)
            {
              rc_xid_valid = u_check_xid (&ll_unitdata_ind->sdu, cr_bit,
                command);

              PFREE (ll_unitdata_ind);

              if (rc_xid_valid)
              {
                if ((llc_data->decoded_l3_xid.valid EQ TRUE) AND
                  !(llc_data->u->xid_tag_sent & (0x00000001L << XID_LAYER_3)))
                {
                  /*
                   * No check needed, reset_received cannot be TRUE
                   * (<R.LLC.XID_INVA.A.003>) and xid_ind is meaningless
                   * (LL_ESTABLISH_IND contains N201-U/I).
                   */
                  u_eval_xid (cr_bit, &reset_received, &xid_ind);

                  /*
                   * Label S_EST_IND
                   */
                  u_label_s_est_ind (pf_bit);
                }
              }
            }
            else /* pf_bit is not set */
            {
              PFREE (ll_unitdata_ind);
            }
            break;

          case U_DISC: /* COMMAND LOCAL_ESTABLISHMENT */
            PFREE (ll_unitdata_ind);

            if (pf_bit EQ 1)
            {
              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 = LL_RELCS_NORMAL;
#else
              ll_release_ind->cause = LL_RELCS_NORMAL;
#endif

              TRACE_1_OUT_PARA("s:%d", ll_release_ind->sapi);
              PSEND (hCommSNDCP, ll_release_ind);

              u_send_dm (1);

              SET_STATE (U, U_ADM);
            }
            break;

          case U_XID: /* COMMAND  LOCAL_ESTABLISHMENT */
            /*
             * Label XID_SABM_SEND
             */
            u_label_xid_sabm_send (ll_unitdata_ind, cr_bit, pf_bit);
            break;

          default:
            if (pf_bit EQ 1)
            {
              llc_data->u->frmr_reason = FRMR_UNDEFINED_CTRL;

              /*
               * Label FRMR_COND
               */
              u_label_frmr_cond (ll_unitdata_ind, cr_bit,
                llc_data->u->frmr_reason);
            }
            else
            {
              PFREE (ll_unitdata_ind);
            }
            break;
        }
      }
      else /* SGSN_RESPONSE */
      {
        /*
         * Label LEST_RESPONSE
         */
        switch (command)
        {
          case U_UA: /* RESPONSE LOCAL_ESTABLISHMENT */
            /*
             * Label LEST_UA
             */

            if (pf_bit EQ 1)
            {
              rc_xid_valid = u_check_xid (&ll_unitdata_ind->sdu,
                                          cr_bit, command);

              if (rc_xid_valid)
              {
                l3p_outstanding = (llc_data->u->xid_tag_sent & (0x1L << XID_LAYER_3)) ? TRUE : FALSE;

                /*
                 * An UA response frame should only be handled if the XID L3
                 * para are included or not same as requested. Else ignore it!
                 */
                if (llc_data->decoded_l3_xid.valid == l3p_outstanding)
                {
                  /*
                   * <R.LLC.ABMEST_R.A.018>
                   */
                  sig_u_t200_stop_req();

                  /*
                   * If an XID parameter is not included in the response and
                   * if it was included in the request, the requested value is
                   * implicit accepted.
                   */
                  u_handle_optimization ();

                  /*
                   * Reset_received cannot be TRUE, otherwise
                   * frame would not be valid
                   */
                  u_eval_xid (cr_bit, &reset_received, &xid_ind);

                  PFREE (ll_unitdata_ind);

                  if (llc_data->u->release_requested == TRUE)
                  {
                    /*
                     * Do not send any establish confirmation to L3 and start
                     * release procedure
                     */
                    llc_data->u->retransmission_counter = 0;

                    SET_STATE (U, U_LOCAL_RELEASE);

                    /*
                     * Send GRLC_DATA_REQ (DISC), start T200.
                     */
                    u_send_disc();
                  }
                  else
                  {
                    if (llc_data->u->ind_cnf_establishment EQ IND_ESTABLISHMENT)
                    {
                      u_send_ll_establish_ind ();

                      SET_STATE (U, U_ESTABLISH_RES_PENDING);

                      /*
                       * start ABM (re-)establish when establish response is received
                       */
                    }
                    else /* CNF_ESTABLISHMENT */
                    {
                      u_send_ll_establish_cnf ();

                      SET_STATE (U, U_ABM);

                      /*
                       * inform LLME about (re-)establischment
                       */
                      sig_u_llme_abmest_ind();
                    }
                  }
                }
                else
                {
                  PFREE (ll_unitdata_ind);
                }
              }
              else
              {
                PFREE (ll_unitdata_ind);

                /*
                 * request T200 to let the timer manually expire,
                 * thus retransmitting the frame immediately
                 */
                sig_u_t200_expire_req ();

                /* SET_STATE (U, SAME_STATE); */
              }
            }
            else /* pf_bit == 0 */
            {
              PFREE (ll_unitdata_ind);

              u_send_llgmm_status_ind (LLGMM_ERRCS_MULT_ASS_TLLI);

              /* SET_STATE (U, SAME_STATE); */
            }
            break;

          case U_DM: /* RESPONSE LOCAL_ESTABLISHMENT */
            /*
             * Label LEST_DM
             */
            PFREE (ll_unitdata_ind);

            if (pf_bit EQ 1)
            {
              sig_u_t200_stop_req();

              if (llc_data->u->release_requested == TRUE)
              {
                u_send_ll_release_cnf ();
              }
              else
              {
                u_send_ll_release_ind (LL_RELCS_DM_RECEIVED);
              }

              SET_STATE (U, U_ADM);
            }
            break;

          case U_XID: /* RESPONSE LOCAL_ESTABLISHMENT */
           /*
            * Label XIDRES
            */
            u_label_xidres (ll_unitdata_ind, command, cr_bit, pf_bit);
            break;

          case U_FRMR: /* RESPONSE LOCAL_ESTABLISHMENT */
            PFREE (ll_unitdata_ind);
            u_send_llgmm_status_ind (LLGMM_ERRCS_FRMR_RECEIVED);
            break;

          default:
            llc_data->u->frmr_reason = FRMR_UNDEFINED_CTRL;

            /*
             * Label FRMR_COND
             */
            u_label_frmr_cond (ll_unitdata_ind, cr_bit,
              llc_data->u->frmr_reason);
            break;
        }
      }
      break; /* U_LOCAL_ESTABLISHMENT */

    case U_ABM:
      if (cr_bit EQ SGSN_COMMAND)
      {
        /*
         * Label ABM_COMMAND
         */
        switch (command)
        {
          case U_SABM: /* COMMAND ABM */
            /*
             * Label ABM_REEST_PEER
             */
            if (pf_bit EQ 1)
            {
              u_send_llgmm_status_ind (LLGMM_ERRCS_PEER_REEST);

              rc_xid_valid = u_check_xid (&ll_unitdata_ind->sdu,
                cr_bit, command);

              PFREE (ll_unitdata_ind);

              if (rc_xid_valid)
              {
                /*
                 * No check needed, reset_received cannot be TRUE
                 * (<R.LLC.XID_INVA.A.003>) and xid_ind is meaningless
                 * (LL_ESTABLISH_IND contains N201-U/I).
                 */
                u_eval_xid (cr_bit, &reset_received, &xid_ind);

                /*
                 * Label S_EST_IND
                 */
                u_label_s_est_ind (pf_bit);
              }
            }
            else /* pf_bit == 0 */
            {
              PFREE (ll_unitdata_ind);
            }
            break;

          case U_DISC: /* COMMAND ABM */
            /*
             * Label ABM_DISC
             */
            PFREE (ll_unitdata_ind);

            if (pf_bit EQ 1)
            {
              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 = LL_RELCS_NORMAL;
#else
              ll_release_ind->cause = LL_RELCS_NORMAL;
#endif
              TRACE_1_OUT_PARA("s:%d", ll_release_ind->sapi);
              PSEND (hCommSNDCP, ll_release_ind);

              SET_STATE (U, U_ADM);

              sig_u_llme_abmrel_ind();

              /*
               * Send UA response without XID information field (= FALSE).
               */
              u_send_ua (pf_bit, FALSE);
            }
            break;

          case U_XID: /* COMMAND  ABM */
            /*
             * Label XID
             */
            u_label_xid (ll_unitdata_ind, command, cr_bit, pf_bit);
            break;

          default:
            if (pf_bit EQ 1)
            {
              llc_data->u->frmr_reason = FRMR_UNDEFINED_CTRL_ABM;

              /*
               * Label FRMR_COND_REEST
               */
              u_label_frmr_cond_reest (ll_unitdata_ind, cr_bit,
                llc_data->u->frmr_reason);
            }
            else
            {
              PFREE (ll_unitdata_ind);
            }
            break;
        }
      }
      else /* SGSN_RESPONSE */
      {
        /*
         * Label ABM_RESPONSE
         */
        switch (command)
        {
          case U_UA: /* RESPONSE ABM */
            /*
             * Label ABM_UA
             */
            PFREE (ll_unitdata_ind);
            u_send_llgmm_status_ind (LLGMM_ERRCS_MULT_ASS_TLLI);
            break;

          case U_DM: /* RESPONSE ABM */
            /*
             * Label ABM_DM
             */
            PFREE (ll_unitdata_ind);

            if (pf_bit EQ 1)
            {
              u_send_llgmm_status_ind (LLGMM_ERRCS_DM1_RECEIVED);
            }
            else /* pf_bit == 0 */
            {
              u_send_llgmm_status_ind (LLGMM_ERRCS_DM0_RECEIVED_REEST);

              llc_data->u->retransmission_counter = 0;

              /*
               * Send LL_ESTABLISH_IND after successful establishment.
               */
              llc_data->u->ind_cnf_establishment = IND_ESTABLISHMENT;

              SET_STATE (U, U_LOCAL_ESTABLISHMENT);

              sig_u_llme_abmrel_ind();

              /*
               * Re-establishment procedure: send GRLC_DATA_REQ (SABM),
               * start T200.
               */
              u_send_sabm();
            }
            break;

          case U_XID: /* RESPONSE ABM */
           /*
            * Label XIDRES
            */
            u_label_xidres (ll_unitdata_ind, command, cr_bit, pf_bit);
            break;

          case U_FRMR: /* RESPONSE ABM */
            PFREE (ll_unitdata_ind);
            u_send_llgmm_status_ind (LLGMM_ERRCS_FRMR_RECEIVED);
            break;

          default:
            llc_data->u->frmr_reason = FRMR_UNDEFINED_CTRL_ABM;

            /*
             * Label FRMR_COND_REEST
             */
            u_label_frmr_cond_reest (ll_unitdata_ind, cr_bit,
              llc_data->u->frmr_reason);
            break;
        }
      }
      break; /* U_ABM */

    case U_LOCAL_RELEASE:
      if (cr_bit EQ SGSN_COMMAND)
      {
        /*
         * Label LREL_COMMAND
         */
        switch (command)
        {
          case U_SABM: /* COMMAND LOCAL_RELEASE */
            PFREE (ll_unitdata_ind);

            if (pf_bit EQ 1)
            {
              /*
               * Send DM response with F bit set to 1.
               */
              u_send_dm (1);
            }
            break;

          case U_DISC: /* COMMAND LOCAL_RELEASE */
            PFREE (ll_unitdata_ind);

            if (pf_bit EQ 1)
            {
              /*
               * Send UA response with F bit set to 1, but without information
               * field (= FALSE).
               */
              u_send_ua (1, FALSE);
            }
            break;

          case U_XID: /* COMMAND  LOCAL_RELEASE */
            /*
             * Label XID
             */
            u_label_xid (ll_unitdata_ind, command, cr_bit, pf_bit);
            break;

          default:
            if (pf_bit EQ 1)
            {
              llc_data->u->frmr_reason = FRMR_UNDEFINED_CTRL;

              /*
               * Label FRMR_COND
               */
              u_label_frmr_cond (ll_unitdata_ind, cr_bit,
                llc_data->u->frmr_reason);
            }
            else
            {
              PFREE (ll_unitdata_ind);
            }
            break;
        }
      }
      else /* SGSN_RESPONSE */
      {
        /*
         * Label LREL_RESPONSE
         */
        switch (command)
        {
          case U_UA: /* RESPONSE LOCAL_RELEASE */
            /*
             * no break!
             */
          case U_DM: /* RESPONSE LOCAL_RELEASE */
            /*
             * Label LREL_UA_DM
             */
            PFREE (ll_unitdata_ind);

            if (pf_bit EQ 1)
            {
              SET_STATE (U, U_ADM);

              sig_u_t200_stop_req();

              u_send_ll_release_cnf();
            }
            else /* pf_bit == 0 */
            {
              if (command EQ U_UA)
              {
                u_send_llgmm_status_ind (LLGMM_ERRCS_MULT_ASS_TLLI);
              }
            }
            break;

          case U_XID: /* RESPONSE LOCAL_RELEASE */
           /*
            * Label XIDRES
            */
            u_label_xidres (ll_unitdata_ind, command, cr_bit, pf_bit);
            break;

          case U_FRMR: /* RESPONSE LOCAL_RELEASE */
            PFREE (ll_unitdata_ind);
            u_send_llgmm_status_ind (LLGMM_ERRCS_FRMR_RECEIVED);
            break;

          default:
            llc_data->u->frmr_reason = FRMR_UNDEFINED_CTRL;

            /*
             * Label FRMR_COND
             */
            u_label_frmr_cond (ll_unitdata_ind, cr_bit,
              llc_data->u->frmr_reason);
            break;
        }
      }
      break; /* U_LOCAL_RELEASE */

    default:
      PFREE (ll_unitdata_ind);
      TRACE_ERROR( "SIG_RX_U_DATA_IND unexpected" );
      break;
  }
} /* sig_rx_u_data_ind() */


/*
+------------------------------------------------------------------------------
| Function    : sig_rx_u_frmr_ind
+------------------------------------------------------------------------------
| Description : Handles the internal signal SIG_RX_U_FRMR_IND
|
| Parameters  : ll_unitdata_ind   - valid pointer to the frame that caused the
|                                   frame rejection condition
|               pdu_type          - type of frame
|               frmr_ctrl_length  - length of frame control field, if known
|               cr_bit            - C/R bit setting of frame
|               frmr_reason       - reason of frame rejection condition
|
+------------------------------------------------------------------------------
*/
GLOBAL void sig_rx_u_frmr_ind (T_LL_UNITDATA_IND *ll_unitdata_ind,
                               T_PDU_TYPE pdu_type,
                               USHORT frmr_ctrl_length,
                               T_BIT cr_bit,
                               UBYTE frmr_reason)
{
  TRACE_ISIG( "sig_rx_u_frmr_ind" );

  /*
   * Set TLLI for current transaction.
   */
  llc_data->u->current_tlli = ll_unitdata_ind->tlli;

  switch (GET_STATE (U))
  {
    case U_ADM:
    case U_LOCAL_ESTABLISHMENT:
    case U_ESTABLISH_RES_PENDING:
    case U_REMOTE_ESTABLISHMENT:
      /*
       * No break!
       */
    case U_LOCAL_RELEASE:
      u_send_llgmm_status_ind (LLGMM_ERRCS_FRMR_COND);

      u_send_frmr (ll_unitdata_ind, pdu_type, frmr_ctrl_length,
        llc_data->sapi->vs, llc_data->sapi->vr,
        cr_bit, frmr_reason);

      PFREE (ll_unitdata_ind);
      break;
    case U_ABM:
      u_send_llgmm_status_ind (LLGMM_ERRCS_FRMR_COND_REEST);

      SET_STATE (U, U_LOCAL_ESTABLISHMENT);

      /*
       * Set bit W4 in frmr_reason to indicate ABM mode.
       */
      frmr_reason |= FRMR_W4;

      u_send_frmr (ll_unitdata_ind, pdu_type, frmr_ctrl_length,
        llc_data->sapi->vs, llc_data->sapi->vr,
        cr_bit, frmr_reason);

      sig_u_llme_abmrel_ind();

      PFREE (ll_unitdata_ind);

      llc_data->u->retransmission_counter = 0;

      /*
       * Send LL_ESTABLISH_IND after successful establishment.
       */
      llc_data->u->ind_cnf_establishment = IND_ESTABLISHMENT;

      /*
       * Re-establishment procedure: send GRLC_DATA_REQ (SABM), start T200.
       */
      u_send_sabm();
      break;
    default:
      PFREE (ll_unitdata_ind);
      TRACE_ERROR( "SIG_RX_U_FRMR_IND unexpected" );
      break;
  }

} /* sig_rx_u_frmr_ind() */


/*
+------------------------------------------------------------------------------
| Function    : sig_t200_u_expired_ind
+------------------------------------------------------------------------------
| Description : Handles the internal signal SIG_T200_U_EXPIRED_IND
|
| Parameters  : grlc_data_req - a valid pointer to the frame (primitive) that
|                               is associated with the timer
|
+------------------------------------------------------------------------------
*/
GLOBAL void sig_t200_u_expired_ind
(
#ifndef LL_DESC
  T_LL_UNITDATA_REQ  *ll_unitdesc_req,
#else
  T_LL_UNITDESC_REQ  *ll_unitdesc_req,
#endif
  UBYTE cause,
  T_EXPIRY_MODE_TYPE mode
)
{
  UBYTE  u_state;

  TRACE_ISIG( "sig_t200_u_expired_ind" );

  /*
   * Set TLLI for current transaction.
   */
  llc_data->u->current_tlli = llc_data->tlli_new;

  u_state = GET_STATE(U);
  switch (u_state)
  {
    case U_LOCAL_ESTABLISHMENT:
      /*
       * First check if an L3 release requested is pendling
       */
      if (llc_data->u->release_requested == TRUE)
      {
        /*
         * Do not restart timer. Send the release confirm.
         */
#ifdef LL_DESC
        llc_cl_desc3_free((T_desc3*)ll_unitdesc_req->desc_list3.first);
#endif /* LL_DESC */
        PFREE (ll_unitdesc_req);

        u_send_ll_release_cnf();

        SET_STATE (U, U_ADM);
      }
      else
      {
        /*
         * Increment retransmission_counter.
         */
        llc_data->u->retransmission_counter++;

        if (llc_data->u->retransmission_counter <= *(llc_data->n200))
        {
          /*
           * 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, cause);

          sig_u_tx_data_req (ll_unitdesc_req, cause);
        }
        else /* retransmission_counter exceeds N200 */
        {
#ifdef LL_DESC
          llc_cl_desc3_free((T_desc3*)ll_unitdesc_req->desc_list3.first);
#endif /* LL_DESC */
          PFREE (ll_unitdesc_req);

          /*
           * Label LEST_N200
           */

          u_send_llgmm_status_ind (LLGMM_ERRCS_SABM_NO_PEER_RES);

          if (mode == EXPIRY_TIMED)
          {
            u_send_ll_release_ind (LL_RELCS_NO_PEER_RES);
          }
          else /* EXPIRY_REQUESTED */
          {
            /*
             * <R.LLC.ABMEST_R.A.015>
             * vs. <R.LLC.ABMEST_R.A.030>
             */
            u_send_ll_release_ind (LL_RELCS_INVALID_XID);
          }

          SET_STATE (U, U_ADM);
        }
      }
      break; /* U_LOCAL_ESTABLISHMENT */

    case U_LOCAL_RELEASE:
      /*
       * First check if an L3 release requested is pendling
       */
      if (llc_data->u->release_requested == TRUE)
      {
        /*
         * Do not restart timer. Send the release confirm.
         */
#ifdef LL_DESC
        llc_cl_desc3_free((T_desc3*)ll_unitdesc_req->desc_list3.first);
#endif /* LL_DESC */
        PFREE (ll_unitdesc_req);

        u_send_ll_release_cnf();

        SET_STATE (U, U_ADM);
      }
      else
      {
        /*
         * Increment retransmission_counter.
         */
        llc_data->u->retransmission_counter++;

        if (llc_data->u->retransmission_counter <= *(llc_data->n200))
        {
          /*
           * 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, cause);
          sig_u_tx_data_req (ll_unitdesc_req, cause);
        }
        else /* retransmission_counter exceeds N200 */
        {
#ifdef LL_DESC
          llc_cl_desc3_free((T_desc3*)ll_unitdesc_req->desc_list3.first);
#endif /* LL_DESC */
          PFREE (ll_unitdesc_req);

          SET_STATE (U, U_ADM);

          u_send_llgmm_status_ind (LLGMM_ERRCS_DISC_NO_PEER_RES);
          u_send_ll_release_cnf ();
        }
      }
      break;

    case U_ADM:
    case U_ABM:
      llc_data->u->retransmission_counter++;
      if (llc_data->u->retransmission_counter <= *(llc_data->n200))
      {
        /* If T200 expires and needs to send SABM the state should
         * be set properly
         */
        if (u_state == U_ADM AND
            llc_data->u->xid_pending NEQ TRUE )
        {
          SET_STATE (U,U_LOCAL_ESTABLISHMENT);
        }
        /*
         * 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, cause);
        sig_u_tx_data_req (ll_unitdesc_req, cause);

        /* SET_STATE (U, SAME_STATE); */
      }
      else
      {
#ifdef LL_DESC
        llc_cl_desc3_free((T_desc3*)ll_unitdesc_req->desc_list3.first);
#endif /* LL_DESC */
        PFREE (ll_unitdesc_req);

        /*
         * Label XIDSENT_N200
         */

        u_send_llgmm_status_ind (LLGMM_ERRCS_XID_NO_PEER_RES);

        if (llc_data->u->xid_pending)
        {
          llc_data->u->xid_pending = FALSE;
        }

        if (u_state == U_ADM)
        {
          if (llc_data->u->xid_tag_sent & (0x00000001L << XID_LAYER_3))
          {
            if (mode == EXPIRY_TIMED)
            {
              u_send_ll_status_ind (LL_ERRCS_NO_PEER_RES);
            }
            else /* EXPIRY_REQUESTED */
            {
              u_send_ll_status_ind (LL_ERRCS_INVALID_XID);
            }
          }

          /* SET_STATE (U, SAME_STATE); */
        }
        else /* U_ABM */
        {
          if (mode == EXPIRY_TIMED)
          {
            u_send_ll_release_ind (LL_RELCS_NO_PEER_RES);
          }
          else /* EXPIRY_REQUESTED */
          {
            /*
             * <R.LLC.XIDNEG_O.A.016>
             * vs. <R.LLC.XIDNEG_O.A.022>
             */
            u_send_ll_release_ind (LL_RELCS_INVALID_XID);
          }

          SET_STATE (U, U_ADM);

          sig_u_llme_abmrel_ind();
        }
      }
      break;

    default:
#ifdef LL_DESC
      llc_cl_desc3_free((T_desc3*)ll_unitdesc_req->desc_list3.first);
#endif /* LL_DESC */
      PFREE (ll_unitdesc_req);
      TRACE_ERROR( "SIG_T200_U_EXPIRED_IND unexpected" );
      break;
  }
} /* sig_t200_u_expired_ind() */


/*
+------------------------------------------------------------------------------
| Function    : sig_llme_u_reest_req
+------------------------------------------------------------------------------
| Description : Handles the internal signal SIG_LLME_U_REEST_REQ
|
| Parameters  :
|
+------------------------------------------------------------------------------
*/
GLOBAL void sig_llme_u_reest_req ( void )
{
  TRACE_FUNCTION( "sig_llme_u_reest_req" );

  switch (GET_STATE(U))
  {
    case U_ABM:
      llc_data->u->retransmission_counter = 0;

      /*
       * Send LL_ESTABLISH_IND after successful re-establishment.
       */
      llc_data->u->ind_cnf_establishment = IND_ESTABLISHMENT;

      SET_STATE (U, U_LOCAL_ESTABLISHMENT);

      /*
       * Send GRLC_DATA_REQ (SABM), start T200.
       */
      u_send_sabm();
      break;

    default:
      TRACE_ERROR( "SIG_LLME_U_REEST_REQ unexpected" );
      break;
  }

} /* sig_llme_u_reest_req() */

/*
+------------------------------------------------------------------------------
| Function    : sig_irx_u_no_frame_expected_ind
+------------------------------------------------------------------------------
| Description : Handles the internal signal SIG_IRX_U_NO_FRAME_EXPECTED_IND
|
| Parameters  :
|
+------------------------------------------------------------------------------
*/
GLOBAL void sig_irx_u_no_frame_expected_ind ( void )
{
  TRACE_FUNCTION( "sig_irx_u_no_frame_expected_ind" );

  switch (GET_STATE(U))
  {
    case U_ADM:
      /*
       * An LLE shall transmit a DM resonse to any valid command received
       * that it cannot action: GSM 04.64 Ver 6.7.0 Chapter 6.4.1.4
       * Upon receiption of an I+S frame in ADM state, the MS shall send
       * a DM response with F bit set 0.
       */
      u_send_dm (0);
      break;

    case U_LOCAL_ESTABLISHMENT:
    case U_ESTABLISH_RES_PENDING:
    case U_REMOTE_ESTABLISHMENT:
      /*
       * Ignore S- and I/S-frames received while ABM establishment. Do
       * not send any response: GSM 04.64 Ver 6.7.0 Chapter 8.5.1.1
       */
      break;

    case U_LOCAL_RELEASE:
      /*
       * Ignore S- and I/S-frames received while ABM termination. Do
       * not send any response: GSM 04.64 Ver 6.7.0 Chapter 8.5.2.1
       */
      break;

    case U_ABM:
    default:
      TRACE_ERROR( "SIG_IRX_U_NO_FRAME_EXPECTED_IND unexpected" );
      break;
  }

} /* sig_irx_u_no_frame_expected_ind() */