view src/g23m-gprs/llc/llc_txs.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 (TX-statemachine)
+----------------------------------------------------------------------------- 
*/ 

#ifndef LLC_TXS_C
#define LLC_TXS_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_txf.h"    /* to get local TX functions */
#include "llc_txl.h"    /* to get local TX labels */
#include "llc_uitxs.h"  /* to get signal interface to UITX */
#include "llc_itxs.h"   /* to get signal interface to ITX */

#ifdef REL99 
#include "llc_uf.h"     /* to get interface to U control frame */
#endif /* REL99 */

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

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

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

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


/*
+------------------------------------------------------------------------------
| Function    : sig_llme_tx_assign_req
+------------------------------------------------------------------------------
| Description : Handles the internal signal SIG_LLME_TX_ASSIGN_REQ
|
| Parameters  : 
|
+------------------------------------------------------------------------------
*/
GLOBAL void sig_llme_tx_assign_req (void)
{
  T_SAPI sapi;


  TRACE_ISIG( "sig_llme_tx_assign_req" );

  switch( GET_STATE( TX ) )
  {
    case TX_TLLI_UNASSIGNED_NOT_READY:
      SET_STATE (TX, TX_TLLI_ASSIGNED_NOT_READY);

      /*
       * ATTENTION:
       * The following loops implies that all SAPIs are odd numbers beginning
       * with 1!
       */
      for (sapi = LL_SAPI_1; sapi <= LL_SAPI_11; sapi += 2)
      {
        SWITCH_SERVICE (llc, uitx, UIMAP(sapi));
        llc_data->current_sapi = sapi;

        sig_tx_uitx_ready_ind();
      }

      for (sapi = LL_SAPI_3; sapi <= LL_SAPI_11; sapi += 2)
      {
        SWITCH_SERVICE (llc, itx, IMAP(sapi));
        llc_data->current_sapi = sapi;

        if (sapi != LL_SAPI_7)
        {
          sig_tx_itx_ready_ind();
        }
      }
      break;
    case TX_TLLI_UNASSIGNED_READY:
      SET_STATE (TX, TX_TLLI_ASSIGNED_READY);

      /*
       * ATTENTION:
       * The following loops implies that all SAPIs are odd numbers beginning
       * with 1!
       */
      for (sapi = LL_SAPI_1; sapi <= LL_SAPI_11; sapi += 2)
      {
        SWITCH_SERVICE (llc, uitx, UIMAP(sapi));
        llc_data->current_sapi = sapi;

        sig_tx_uitx_ready_ind();
      }

      for (sapi = LL_SAPI_3; sapi <= LL_SAPI_11; sapi += 2)
      {
        SWITCH_SERVICE (llc, itx, IMAP(sapi));
        llc_data->current_sapi = sapi;

        if (sapi != LL_SAPI_7)
        {
          sig_tx_itx_ready_ind();
        }
      }
      break;

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

  return;
} /* sig_llme_tx_assign_req() */


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

  switch( GET_STATE( TX ) )
  {
    case TX_TLLI_ASSIGNED_NOT_READY:
      tx_clear_buffer();
      SET_STATE (TX, TX_TLLI_UNASSIGNED_NOT_READY);
      break;

    case TX_TLLI_ASSIGNED_READY:
      tx_clear_buffer();
      SET_STATE (TX, TX_TLLI_UNASSIGNED_READY);
      break;

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

  return;
} /* sig_llme_tx_unassign_req() */


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

  switch( GET_STATE( TX ) )
  {
    case TX_TLLI_ASSIGNED_NOT_READY:
      /*
       * No break!
       */
    case TX_TLLI_ASSIGNED_READY:
      tx_clear_buffer();
      //sig_tx_uitx_ready_ind();
      break;

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

  return;
} /* sig_llme_tx_reset_req() */


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

  switch( GET_STATE( TX ) )
  {
    case TX_TLLI_ASSIGNED_NOT_READY:
      /*
       * No break!
       */
    case TX_TLLI_ASSIGNED_READY:
      sig_tx_uitx_ready_ind();
      break;

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

  return;
} /* sig_llme_tx_ready_req() */




/*
+------------------------------------------------------------------------------
| Function    : sig_u_tx_data_req
+------------------------------------------------------------------------------
| Description : Handles the internal signal SIG_U_TX_DATA_REQ
|
| Parameters  : ll_unitdata_req - a valid pointer to a GRLC-UNITDATA-REQ 
|                                 primitive
|               cause           - RLC/MAC cause
|
+------------------------------------------------------------------------------
*/
GLOBAL void sig_u_tx_data_req 
(
#ifdef LL_DESC
 T_LL_UNITDESC_REQ *ll_unitdesc_req,
#else
 T_LL_UNITDATA_REQ *ll_unitdesc_req,
#endif
 UBYTE cause
)
{
  ULONG             reservation_no;
  BOOL              buffer_available;   
  UBYTE             protected_mode;

  TRACE_ISIG( "sig_u_tx_data_req" );
  
  switch( GET_STATE( TX ) )
  {
    case TX_TLLI_ASSIGNED_NOT_READY:
      /* No break. */
    case TX_TLLI_ASSIGNED_READY:

      /*
       * U frames are always sent with default cause! except cell notification.
       */
#ifdef REL99 
      if (cause EQ GRLC_DTACS_CELL_NOTIFI_NULL_FRAME)
      {
        tx_reserve_buffer (ll_unitdesc_req, PRIM_DATA, cause, 
                          SERVICE_U, &reservation_no, &buffer_available);
      }
      else
#endif /* REL99 */
      {
        tx_reserve_buffer (ll_unitdesc_req, PRIM_DATA, GRLC_DTACS_DEF, 
                          SERVICE_U, &reservation_no, &buffer_available);
      }
      /*
       * U frames are always sent in LLC protected mode.
       */
      protected_mode = CCI_PM_PROTECTED;

      {
#ifdef LL_DESC 
        T_CCI_CIPHER_DESC_REQ *cci_cipher_desc_req;
        MALLOC(cci_cipher_desc_req, sizeof(T_CCI_CIPHER_DESC_REQ));
        cci_cipher_desc_req->desc_list3.first = ll_unitdesc_req->desc_list3.first;
        cci_cipher_desc_req->desc_list3.list_len = ll_unitdesc_req->desc_list3.list_len;
        cci_cipher_desc_req->attached_counter = ll_unitdesc_req->attached_counter;
        /*
         * decrease attached counter. If no one is still attached
         * free the primitive memory
         */
        if (ll_unitdesc_req->attached_counter == CCI_NO_ATTACHE)
        {
          PFREE(ll_unitdesc_req);
        } else {
          TRACE_0_INFO("LL_UNITDESC_REQ still attached");
        }
#else
        PPASS (ll_unitdesc_req, cci_cipher_desc_req, CCI_CIPHER_REQ);
#endif
        /*
         * Header size (offset of information) is reqired, if ciphering is used
         * or if the frame shall be send in unprotected mode. In this case both
         * is not true and so it can be ignored.
         */
        cci_cipher_desc_req->header_size = 0;

        /*
         * Associate reservation_no with the frame.
         */
        cci_cipher_desc_req->reference1 = reservation_no;

        /*
         * U frames are never sent ciphered, thus define LL_CIPHER_OFF and set
         * N(S) to 0 (it is not used).
         */
        tx_send_cipher_req (cci_cipher_desc_req, U_FRAME, protected_mode, 0,
          LL_CIPHER_OFF, 0);

      }
      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_U_TX_DATA_REQ unexpected" );
      break;
  }
} /* sig_u_tx_data_req() */


/*
+------------------------------------------------------------------------------
| Function    : sig_uitx_tx_data_req
+------------------------------------------------------------------------------
| Description : Handles the internal signal SIG_UITX_TX_DATA_REQ
|
| Parameters  : ll_unitdata_req - a valid pointer to a GRLC-UNITDATA-REQ 
|                                 primitive
|               cipher - indicates if the frame shall be ciphered or not
|
+------------------------------------------------------------------------------
*/
GLOBAL void sig_uitx_tx_data_req 
  (
#ifdef LL_DESC
  T_LL_UNITDESC_REQ *ll_unitdesc_req,
#else
  T_LL_UNITDATA_REQ *ll_unitdesc_req,
#endif
  UBYTE             cipher,
  UBYTE             cause,
  T_FRAME_NUM       nu,
  ULONG             oc
  )
 
{
  ULONG             reservation_no;
  BOOL              buffer_available;
  UBYTE             protected_mode;


  TRACE_ISIG( "sig_uitx_tx_data_req" );
  
  switch( GET_STATE( TX ) )
  {
    case TX_TLLI_ASSIGNED_NOT_READY:
      /* No break. */
    case TX_TLLI_ASSIGNED_READY:

      tx_reserve_buffer (ll_unitdesc_req, PRIM_DATA, cause, SERVICE_UITX,
        &reservation_no, &buffer_available);

      if (ll_unitdesc_req->ll_qos.relclass EQ LL_NO_REL)
      {
        protected_mode = CCI_PM_UNPROTECTED;
      }
      else /* reliability classes 4 - 1 */
      {
        protected_mode = CCI_PM_PROTECTED;
      }

      {
#ifdef LL_DESC        
        T_CCI_CIPHER_DESC_REQ *cci_cipher_desc_req;
        MALLOC(cci_cipher_desc_req, sizeof(T_CCI_CIPHER_DESC_REQ));
        cci_cipher_desc_req->desc_list3.first = ll_unitdesc_req->desc_list3.first;
        cci_cipher_desc_req->desc_list3.list_len = ll_unitdesc_req->desc_list3.list_len;
        cci_cipher_desc_req->attached_counter = ll_unitdesc_req->attached_counter;
        /*
         * decrease attached counter. If no one is still attached
         * free the primitive memory
         */
        if (ll_unitdesc_req->attached_counter == CCI_NO_ATTACHE)
        {
          PFREE(ll_unitdesc_req);
        } else {
          TRACE_0_INFO("LL_UNITDESC_REQ still attached");
        }
#else
        PPASS (ll_unitdesc_req, cci_cipher_desc_req, CCI_CIPHER_REQ);
#endif
        /*
         * Header size (offset of information) is reqired, if ciphering is used
         * or if the frame shall be send in unprotected mode. 
         */
        cci_cipher_desc_req->header_size = UI_CTRL_MIN_OCTETS;

        /*
         * Associate reservation_no with the frame.
         */
        cci_cipher_desc_req->reference1 = reservation_no;

        tx_send_cipher_req (cci_cipher_desc_req, UI_FRAME, protected_mode, nu, 
          cipher, oc);

        if (buffer_available)
        {
          /*
           * Buffer is still available for current SAPI.
           */
          sig_tx_uitx_ready_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_UITX_TX_DATA_REQ unexpected" );
      break;
  }
} /* sig_uitx_tx_data_req() */


/*
+------------------------------------------------------------------------------
| Function    : sig_uitx_tx_unitdata_req
+------------------------------------------------------------------------------
| Description : Handles the internal signal SIG_UITX_TX_UNITDATA_REQ
|
| Parameters  : ll_unitdata_req - a valid pointer to a GRLC-UNITDATA-REQ 
|                                 primitive
|               cipher - indicates if the frame shall be ciphered or not
|
+------------------------------------------------------------------------------
*/
GLOBAL void sig_uitx_tx_unitdata_req
(
#ifdef LL_DESC
  T_LL_UNITDESC_REQ *ll_unitdesc_req, 
#else
  T_LL_UNITDATA_REQ *ll_unitdesc_req,
#endif  
  UBYTE             cipher,
  T_FRAME_NUM       nu,
  ULONG             oc
) 
  
{
  ULONG             reservation_no;
  BOOL              buffer_available;
  UBYTE             protected_mode;


  TRACE_ISIG( "sig_uitx_tx_unitdata_req" ); 
  
  switch( GET_STATE( TX ) )
  {
    case TX_TLLI_ASSIGNED_NOT_READY:
      /* No break. */
    case TX_TLLI_ASSIGNED_READY:

      /*
       * Parameter cause will be ignored for RLC/MAC unacknowledged frames,
       * so set it to 0.
       */
      tx_reserve_buffer (ll_unitdesc_req, PRIM_UNITDATA, 0, SERVICE_UITX,
        &reservation_no, &buffer_available);

      if (ll_unitdesc_req->ll_qos.relclass EQ LL_NO_REL)
      {
        protected_mode = CCI_PM_UNPROTECTED;
      }
      else /* reliability classes 4 - 1 */
      {
        protected_mode = CCI_PM_PROTECTED;
      }

      {
#ifdef LL_DESC
        T_CCI_CIPHER_DESC_REQ *cci_cipher_desc_req;
        MALLOC(cci_cipher_desc_req, sizeof(T_CCI_CIPHER_DESC_REQ));
        cci_cipher_desc_req->desc_list3.first = ll_unitdesc_req->desc_list3.first;
        cci_cipher_desc_req->desc_list3.list_len = ll_unitdesc_req->desc_list3.list_len;
        cci_cipher_desc_req->attached_counter = ll_unitdesc_req->attached_counter;
        /*
         * decrease attached counter. If no one is still attached
         * free the primitive memory
         */
        if (ll_unitdesc_req->attached_counter == CCI_NO_ATTACHE)
        {
          PFREE(ll_unitdesc_req); 
        } else {
          TRACE_0_INFO("LL_UNITDESC_REQ still attached");
        }
#else
        PPASS (ll_unitdesc_req, cci_cipher_desc_req, CCI_CIPHER_REQ);
#endif
        /*
         * Header size (offset of information) is reqired, if ciphering is used
         * or if the frame shall be send in unprotected mode. 
         */
        cci_cipher_desc_req->header_size = UI_CTRL_MIN_OCTETS;

        /*
         * Associate reservation_no with the frame.
         */
        cci_cipher_desc_req->reference1 = reservation_no;

        tx_send_cipher_req (cci_cipher_desc_req, UI_FRAME, protected_mode, nu, 
          cipher, oc);

        if (buffer_available)
        {
          /*
           * Buffer is still available for current SAPI.
           */
          sig_tx_uitx_ready_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_UITX_TX_UNITDATA_REQ unexpected" );
      break;
  }
} /* sig_uitx_tx_unitdata_req() */


/*
+------------------------------------------------------------------------------
| Function    : sig_itx_tx_data_req
+------------------------------------------------------------------------------
| Description : Handles the internal signal SIG_ITX_TX_DATA_REQ
|
| Parameters  : ll_data_req - a valid pointer to a GRLC-DATA-REQ primitive
|               cause       - 
|
+------------------------------------------------------------------------------
*/
GLOBAL void sig_itx_tx_data_req
(
#ifdef LL_DESC
  T_LL_UNITDESC_REQ *ll_unitdesc_req, 
#else
  T_LL_UNITDATA_REQ *ll_unitdesc_req, 
#endif
  T_PDU_TYPE        frame_type,
  T_FRAME_NUM       ns,
  UBYTE             cause,
  USHORT            header_size,
  ULONG             oc
)
{
  ULONG             reservation_no;
  BOOL              buffer_available;
  UBYTE             protected_mode;

  TRACE_ISIG( "sig_itx_tx_data_req" );
  
  switch( GET_STATE( TX ) )
  {
    case TX_TLLI_ASSIGNED_NOT_READY:
      /* No break. */
    case TX_TLLI_ASSIGNED_READY:

      tx_reserve_buffer (ll_unitdesc_req, PRIM_DATA, cause, SERVICE_ITX,
        &reservation_no, &buffer_available);

      /*
       * I/S + S frames always send in protected mode
       */
      protected_mode = CCI_PM_PROTECTED;

      {
#ifdef LL_DESC
        T_CCI_CIPHER_DESC_REQ *cci_cipher_desc_req;
        MALLOC(cci_cipher_desc_req, sizeof(T_CCI_CIPHER_DESC_REQ));
        cci_cipher_desc_req->desc_list3.first = ll_unitdesc_req->desc_list3.first;
        cci_cipher_desc_req->desc_list3.list_len = ll_unitdesc_req->desc_list3.list_len;
        cci_cipher_desc_req->attached_counter = ll_unitdesc_req->attached_counter;
        /*
         * free the primitive memory
         */
        PFREE(ll_unitdesc_req); 
#else
        PPASS (ll_unitdesc_req, cci_cipher_desc_req, CCI_CIPHER_REQ);
#endif
        /*
         * Header size (offset of information) is reqired, if ciphering is used
         * or if the frame shall be send in unprotected mode. 
         */
        cci_cipher_desc_req->header_size = header_size;

        /*
         * Associate reservation_no with the frame.
         */
        cci_cipher_desc_req->reference1 = reservation_no;

        if (frame_type == S_FRAME)
        {
          /*
           * Do not try to cipher an S frame (there is no information ;-)
           */
          tx_send_cipher_req (cci_cipher_desc_req, frame_type, protected_mode, ns, 
            LL_CIPHER_OFF, oc);
        }
        else
        {
          /*
           * I frames shall always send ciphered
           */
          tx_send_cipher_req (cci_cipher_desc_req, frame_type, protected_mode, ns, 
            LL_CIPHER_ON, oc);
        }

        if (buffer_available)
        {
          /*
           * Buffer is still available for current SAPI.
           */
          sig_tx_itx_ready_ind();
        }

      }
      break;

    default:
      if (ll_unitdesc_req->attached_counter == CCI_NO_ATTACHE)
      {
#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_ITX_TX_DATA_REQ unexpected" );
      break;
  }
} /* sig_itx_tx_data_req() */


/*
+------------------------------------------------------------------------------
| Function    : sig_llme_tx_trigger_req
+------------------------------------------------------------------------------
| Description : Handles the internal signal SIG_LLME_TX_TRIGGER_REQ
|
| Parameters  : cause - trigger cause
|
+------------------------------------------------------------------------------
*/
GLOBAL void sig_llme_tx_trigger_req (UBYTE cause)
{
  T_TX_QUEUE                  *elem;


  TRACE_ISIG( "sig_llme_tx_trigger_req" );
  
  TRACE_1_PARA("cause:%d", cause);

  switch( GET_STATE( TX ) )
  {
    case TX_TLLI_ASSIGNED_NOT_READY:
      /* no break! */
    case TX_TLLI_ASSIGNED_READY:
      tx_get_first_data_frame (&elem);

      if (elem EQ NULL)
      {
#ifdef REL99 
        if (cause EQ LLGMM_TRICS_CELL_UPDATE_NULL_FRAME)
        {
          cause = GRLC_DTACS_CELL_NOTIFI_NULL_FRAME;
          sig_tx_u_send_null(cause);
        }
        else 
#endif /* REL99 */      
        if (cause EQ LLGMM_TRICS_CELL_UPDATE)
        {
           cause = GRLC_DTACS_EMPTY_FRAME;
           sig_tx_uitx_trigger_ind (cause);
        }
        else
        {
           sig_tx_uitx_trigger_ind (cause);
        }
      }
      else /* elem is valid */
      {
        /*
         * NOTE: Possible cause values are DEFAULT, MOBILITY_MANAGEMENT, 
         * PAGE_RESPONSE, CELL_UPDATE and CELL NOTIFICATION.
         * Overwriting DEFAULT, MOBILITY_MANAGEMENT, and the same cause 
         * (which is already set) is ok. This leaves two relevant (and 
         * mutually exclusive) trigger causes to be considered. A problem may 
         * occur if the primitive contained the one remaining trigger cause 
         * and the other is requested. But according to ANS this should not 
         * happen.
         */
        
        if ((cause EQ LLGMM_TRICS_CELL_UPDATE)
#ifdef REL99 
               OR
            (cause EQ LLGMM_TRICS_CELL_UPDATE_NULL_FRAME)
#endif /*REL99*/
            )
        {
          cause = GRLC_DTACS_DEF;
        }

        if (elem->primitive EQ NULL)
        {
          /*
           * Primitive is not yet ready-to-send. Modify cause in primitve 
           * header variable.
           */
          elem->ph_cause = cause;
        }
        else /* elem->primitive is valid */
        {
          /*
           * Primitive is ready-to-send. Modifiy cause in primitive.
           */
          ((T_GRLC_DATA_REQ *)(elem->primitive))->cause = cause;
        }
      }
      break;

    default:
      TRACE_ERROR( "SIG_LLME_TX_TRIGGER_REQ unexpected" );
      break;
  }
} /* sig_llme_tx_trigger_req() */


/*
+------------------------------------------------------------------------------
| Function    : sig_llme_tx_suspend_req
+------------------------------------------------------------------------------
| Description : Handles the internal signal SIG_LLME_TX_SUSPEND_REQ
|
| Parameters  : 
|
+------------------------------------------------------------------------------
*/
GLOBAL void sig_llme_tx_suspend_req (void)
{
  TRACE_ISIG( "sig_llme_tx_suspend_req" );
  
  switch( GET_STATE( TX ) )
  {
    case TX_TLLI_UNASSIGNED_NOT_READY:
    case TX_TLLI_ASSIGNED_NOT_READY:
      /*
       * Nothing to do
       */
      break;

    case TX_TLLI_ASSIGNED_READY:
      /*
       * Flow Control has to be re-started by GRLC
       */
      SET_STATE (TX, TX_TLLI_ASSIGNED_NOT_READY);
      break;

    case TX_TLLI_UNASSIGNED_READY:
      /*
       * Flow Control has to be re-started by GRLC
       */
      SET_STATE (TX, TX_TLLI_UNASSIGNED_NOT_READY);
      break;

    default:
      TRACE_ERROR( "SIG_LLME_TX_SUSPEND_REQ unexpected" );
      break;
  }
} /* sig_llme_tx_suspend_req() */

/*
+------------------------------------------------------------------------------
| Function    : sig_llme_tx_resume_req
+------------------------------------------------------------------------------
| Description : Handles the internal signal SIG_LLME_TX_RESUME_REQ
|
| Parameters  : 
|
+------------------------------------------------------------------------------
*/
GLOBAL void sig_llme_tx_resume_req (BOOL grlc_was_suspened)
{
  T_GRLC_DATA_REQ              *grlc_data_req;
  T_GRLC_UNITDATA_REQ          *grlc_unitdata_req;
  T_PRIM_TYPE                 prim_type;
  T_SERVICE                   rx_service;
  UBYTE                       sapi;

  TRACE_ISIG( "sig_llme_tx_resume_req" );
  
  switch( GET_STATE( TX ) )
  {
    case TX_TLLI_UNASSIGNED_NOT_READY:
    case TX_TLLI_ASSIGNED_NOT_READY:
      /*
       * Nothing to do
       */
      break;

    case TX_TLLI_ASSIGNED_READY:
      if (grlc_was_suspened == FALSE)
      {
        /*
         * LLME indicates that any data may now be sent again.
         */
        do {
          tx_get_next_frame (&grlc_data_req, &grlc_unitdata_req, &prim_type, 
            &rx_service, &sapi);

          switch (prim_type)
          {
            case PRIM_DATA:
              /*
               * Label S_DATA
               */
              tx_label_s_data (rx_service, grlc_data_req);
              break;
            case PRIM_UNITDATA:
              /*
               * Label S_UNITDATA
               */
              tx_label_s_unitdata (rx_service, grlc_unitdata_req);
              break;
            case PRIM_REMOVED:
              /*
               * Label S_REMOVED
               */
              tx_label_s_removed (rx_service, sapi);
              break;
            default: /* NO_PRIM */
              break;
          }
        } while (prim_type == PRIM_REMOVED);
      }
      else
      {
        /*
         * Flow Control has to be re-started by GRLC
         */
        SET_STATE (TX, TX_TLLI_ASSIGNED_NOT_READY);
      }
      break;

    case TX_TLLI_UNASSIGNED_READY:
      /*
       * Flow Control has to be re-started by GRLC
       */
      SET_STATE (TX, TX_TLLI_UNASSIGNED_NOT_READY);
      break;

    default:
      TRACE_ERROR( "SIG_LLME_TX_RESUME_REQ unexpected" );
      break;
  }
} /* sig_llme_tx_resume_req() */



/*
+------------------------------------------------------------------------------
| Function    : sig_llme_tx_flush_req
+------------------------------------------------------------------------------
| Description : Handles the internal signal SIG_LLME_TX_FLUSH_REQ
|
| Parameters  : 
|
+------------------------------------------------------------------------------
*/
GLOBAL void sig_llme_tx_flush_req (T_SERVICE service)
{
  TRACE_ISIG( "sig_llme_tx_flush_req" );
  
  switch( GET_STATE( TX ) )
  {
    case TX_TLLI_ASSIGNED_READY:
    case TX_TLLI_ASSIGNED_NOT_READY:
      tx_remove_data_frames (service, llc_data->current_sapi);
      break;

    default:
      TRACE_ERROR( "SIG_LLME_TX_FLUSH_REQ unexpected" );
      break;
  }
} /* sig_llme_tx_flush_req() */