diff src/g23m-gprs/llc/llc_txf.c @ 1:d393cd9bb723

src/g23m-*: initial import from Magnetite
author Mychaela Falconia <falcon@freecalypso.org>
date Sun, 15 Jul 2018 04:40:46 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/g23m-gprs/llc/llc_txf.c	Sun Jul 15 04:40:46 2018 +0000
@@ -0,0 +1,977 @@
+/* 
++----------------------------------------------------------------------------- 
+|  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 (TX-statemachine)
++----------------------------------------------------------------------------- 
+*/ 
+
+#ifndef LLC_TXF_C
+#define LLC_TXF_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_generate_input */
+#include "llc_txf.h"    /* to get the global entity definitions */
+#include "llc_txp.h"    /* to get the function tx_cci_cipher_cnf */
+
+#include "llc_uitxs.h"  /* to get signal interface to UITX */
+#include "llc_itxs.h"   /* to get signal interface to ITX */
+
+#ifndef TI_PS_OP_CIPH_DRIVER
+#include "cci_fbsf.h"     /* to get functional interface */
+#endif
+
+
+/*==== CONST ================================================================*/
+
+/*==== LOCAL VARS ===========================================================*/
+
+/*==== PRIVATE FUNCTIONS ====================================================*/
+
+/*==== PUBLIC FUNCTIONS =====================================================*/
+
+
+/*
++------------------------------------------------------------------------------
+| Function    : tx_init
++------------------------------------------------------------------------------
+| Description : This procedure initialises all necessary variables of send_pdu.
+|
+| Parameters  : 
+|
++------------------------------------------------------------------------------
+*/
+GLOBAL void llc_tx_init (void)
+{
+  TRACE_FUNCTION( "tx_init" );
+
+  /*
+   * Initialise TX with state NOT_READY.
+   */
+  INIT_STATE (TX, TX_TLLI_UNASSIGNED_NOT_READY);
+
+  /*
+   * Flush TX queue, if there is (in case of an LLC 
+   * restart) old stuff queued.
+   */
+  tx_clear_buffer();
+
+  return;
+} /* tx_init() */
+
+
+/*
++------------------------------------------------------------------------------
+| Function    : tx_clear_buffer
++------------------------------------------------------------------------------
+| Description : This procedure frees all buffered CCI_CIPHER_CNF primitives.
+|
+| Parameters  : 
+|
++------------------------------------------------------------------------------
+*/
+GLOBAL void tx_clear_buffer (void)
+{
+  T_TX_QUEUE        *elem;
+  T_TX_QUEUE        *elem_next;
+
+
+  TRACE_FUNCTION( "tx_clear_buffer" );
+
+  for (elem = llc_data->tx.queue; elem NEQ NULL; elem = elem_next)
+  {
+    elem_next = elem->next;
+
+    /*
+     * Free primitive if any is stored in queue element.
+     */
+    if (elem->primitive NEQ (ULONG)NULL)
+    {
+      PFREE (elem->primitive);
+    }
+
+    MFREE (elem);
+  }
+
+  llc_data->tx.queue = NULL;
+
+  /*
+   * Initialise UITX space counter for each SAPI.
+   */
+  llc_data->tx.queue_counter_uitx[0] = UITX_1_QUEUE_SIZE;
+  llc_data->tx.queue_counter_uitx[1] = UITX_3_QUEUE_SIZE;
+  llc_data->tx.queue_counter_uitx[2] = UITX_5_QUEUE_SIZE;
+  llc_data->tx.queue_counter_uitx[3] = UITX_7_QUEUE_SIZE;
+  llc_data->tx.queue_counter_uitx[4] = UITX_9_QUEUE_SIZE;
+  llc_data->tx.queue_counter_uitx[5] = UITX_11_QUEUE_SIZE;
+
+  /*
+   * Initialise ITX space counter for each SAPI.
+   */
+  llc_data->tx.queue_counter_itx[0] = ITX_3_QUEUE_SIZE;
+  llc_data->tx.queue_counter_itx[1] = ITX_5_QUEUE_SIZE;
+  llc_data->tx.queue_counter_itx[2] = ITX_9_QUEUE_SIZE;
+  llc_data->tx.queue_counter_itx[3] = ITX_11_QUEUE_SIZE;
+
+  return;
+} /* tx_clear_buffer() */
+
+/*
++------------------------------------------------------------------------------
+| Function    : tx_cipher_req
++------------------------------------------------------------------------------
+| Description : Handles the function tx_cipher_req. This functions sets the
+|               ciphering parameters and calls the ciphering driver function.
+|
+| Parameters  : todo
+|
++------------------------------------------------------------------------------
+*/
+GLOBAL void tx_cipher_req
+(
+#ifdef LL_DESC
+T_CCI_CIPHER_DESC_REQ *cipher_req
+#else
+T_CCI_CIPHER_REQ *cipher_req
+#endif
+)
+
+{ 
+  T_CIPH_init_cipher_req_parms  init_cipher_req_parms;
+  T_CIPH_cipher_req_parms       cipher_req_parms;
+  T_CIPH_in_data_list           in_data_list;
+  T_CIPH_out_data               out_data;
+  T_CIPH_ck                     ck;
+  U16                           i;
+  U8                            status;
+
+  TRACE_FUNCTION( "tx_cipher_req" );
+
+#ifdef LLC_TRACE_CIPHERING
+  TRACE_EVENT("UPLINK NON CIPHERED DATA");
+  llc_trace_desc_list3_content(cipher_req->desc_list3);
+#endif
+  /*
+   * Copy pointer to desc's from CIPHER_REQ to the in_data_list
+   * The in_data array in allocated dynamically in this func.
+   */
+  llc_copy_ul_data_to_list(cipher_req, &in_data_list);
+  /*
+   * Store ciphering parameters 
+   */
+  cipher_req_parms.gprs_parameters.pm              = cipher_req->pm;
+  cipher_req_parms.gprs_parameters.header_size     = cipher_req->header_size;
+  cipher_req_parms.gprs_parameters.ciphering_input = cipher_req->ciphering_input;
+  cipher_req_parms.gprs_parameters.threshold       = 0;
+  init_cipher_req_parms.direction = CIPH_UPLINK_DIR;
+  init_cipher_req_parms.algo      = cipher_req->ciphering_algorithm;
+  init_cipher_req_parms.ptr_ck = & ck;
+  /*
+   * Copy ciphering key
+   */
+  for (i=0; i<8;i++){
+    init_cipher_req_parms.ptr_ck->ck_element[i] = cipher_req->kc.key[i];
+  }
+
+  {
+    /* Use GRLC_DATA_REQ instead of CCI_CIPHER_CNF to avoid PPASS in LLC*/
+    PALLOC_SDU (grlc_data_req, GRLC_DATA_REQ,
+                (USHORT)(cipher_req->desc_list3.list_len*8 + FCS_SIZE_BITS));
+  
+    grlc_data_req->sdu.o_buf = 0;
+    grlc_data_req->sdu.l_buf = 0;
+    out_data.buf = (U32)(&grlc_data_req->sdu.buf[grlc_data_req->sdu.o_buf]);
+    /*
+     * Initialize ciphering driver and cipher data
+     */
+#ifdef TI_PS_OP_CIPH_DRIVER
+    ciph_init_cipher_req (&init_cipher_req_parms, NULL);
+    ciph_cipher_req (&cipher_req_parms, &in_data_list, &out_data, &status);
+#else
+    ciph_init_cipher_req_sim (&init_cipher_req_parms, NULL);
+    ciph_cipher_req_sim (&cipher_req_parms, &in_data_list, &out_data, &status);
+#endif
+
+    /*
+     * "Send" CIPHER_CNF to LLC
+     */
+    grlc_data_req->sdu.l_buf = out_data.len * 8;
+    grlc_data_req->tlli = cipher_req->reference1;
+
+#ifdef LLC_TRACE_CIPHERING
+    TRACE_EVENT("UPLINK CIPHERED DATA");
+    llc_trace_sdu(&grlc_data_req->sdu);
+#endif
+    tx_cci_cipher_cnf (grlc_data_req);
+
+  }
+      /*
+       * Remove in use mark from the cipher request primitive
+       */
+  if (cipher_req != NULL) {
+    {
+      /*
+       * Free cipher request, if not further used by other entities
+       */
+      if (cipher_req->attached_counter == CCI_NO_ATTACHE) {
+#ifdef LL_DESC
+        llc_cl_desc3_free((T_desc3*)cipher_req->desc_list3.first);
+#endif /* LL_DESC */
+      }
+    }
+    MFREE (cipher_req);
+    cipher_req = NULL;
+  }
+      /*
+       * Free in_data array from in_data_list 
+       * allocated dynamically in llc_copy_ul_data_to_list() 
+       */
+  if(in_data_list.ptr_in_data != NULL){
+    MFREE(in_data_list.ptr_in_data); 
+    in_data_list.ptr_in_data = NULL;
+  }
+
+} /* tx_cipher_req() */
+
+/*
++------------------------------------------------------------------------------
+| Function    : tx_send_cipher_req
++------------------------------------------------------------------------------
+| Description : This procedure fills all necessary parameters in the primitive 
+|               CCI_CIPHER_REQ and sends the primitive to CCI.
+|
+| Parameters  : cci_cipher_req  - a valid pointer to a CCI_CIPHER_REQ primitive
+|               frame_type      - indicates frame type (e.g. UI_FRAME)
+|               protected_mode  - PM bit setting for CCI_CIPHER_REQ
+|               ns              - N(U) for UI frames
+|               cipher          - indicates if frame shall be ciphered or not
+|               oc              - 
+|
++------------------------------------------------------------------------------
+*/
+GLOBAL void tx_send_cipher_req 
+(
+#ifdef LL_DESC
+  T_CCI_CIPHER_DESC_REQ *cci_cipher_desc_req,
+#else
+  T_CCI_CIPHER_REQ *cci_cipher_desc_req,
+#endif
+  T_PDU_TYPE frame_type,
+  UBYTE protected_mode,
+  T_FRAME_NUM ns,
+  UBYTE cipher,
+  ULONG oc
+)
+{
+  TRACE_FUNCTION( "tx_send_cipher_req" );
+
+  cci_cipher_desc_req->pm = protected_mode;
+
+  if ( (cipher EQ LL_CIPHER_ON)  AND 
+       (llc_data->ciphering_algorithm NEQ LLGMM_CIPHER_NO_ALGORITHM) )
+  {
+    cci_cipher_desc_req->ciphering_algorithm = llc_data->ciphering_algorithm;
+    memcpy (&cci_cipher_desc_req->kc, &llc_data->kc, sizeof(T_kc));
+
+    llc_generate_input (llc_data->current_sapi, frame_type, ns, 
+      &cci_cipher_desc_req->ciphering_input, oc);
+
+    cci_cipher_desc_req->direction = CCI_DIRECTION_UPLINK;
+  }
+  else /* LL_CIPHER_OFF */
+  {
+    cci_cipher_desc_req->ciphering_algorithm = CCI_CIPHER_NO_ALGORITHM;
+  }
+
+  tx_cipher_req (cci_cipher_desc_req);
+  return;
+} /* tx_send_cipher_desc_req() */
+
+
+/*
++------------------------------------------------------------------------------
+| Function    : tx_reserve_buffer
++------------------------------------------------------------------------------
+| Description : This procedure allocates an element for the local transmit 
+|               queue. If cause is DEFAULT or MOBILITY_MANAGEMENT, the element 
+|               is appended at the end of the queue, otherwise it is inserted 
+|               at the beginning. Necessary data like primitive header 
+|               information and additional important data for the (resulting) 
+|               GRLC_xDATA_REQ primitive is stored in the reserved buffer 
+|               element. The primitive pointer in the element is set to NULL, 
+|               to indicate that the element is not yet 'ready to send'. 
+|               Parameter reservation_no will be set to the allocation number 
+|               of the element. Parameter buffer_available will be set to TRUE 
+|               if additional data primitives can be buffered for the current 
+|               sapi, otherwise it will be set to FALSE.
+|
+| Parameters  : ll_unitdata_req   - a valid pointer to a LL_UNITDATA_REQ
+|                                   primitive, containing the data to be sent
+|               prim_type         - indicates GRLC_DATA/UNITDATA_REQ
+|               cause             - frame cause, only valid for GRLC_DATA_REQ
+|               rx_service        - service for flow control (if any)
+|               reservation_no    - associated number for the buffer entry
+|               buffer_available  - still buffer space available for current 
+|                                   SAPI
+|
++------------------------------------------------------------------------------
+*/
+GLOBAL void tx_reserve_buffer 
+  (
+#ifdef LL_DESC                               
+  T_LL_UNITDESC_REQ *ll_unitdesc_req,
+#else
+  T_LL_UNITDATA_REQ *ll_unitdesc_req,
+#endif
+  T_PRIM_TYPE prim_type,
+  UBYTE cause,
+  T_SERVICE rx_service,
+  ULONG *reservation_no,
+  BOOL *buffer_available
+  )
+{
+  T_TX_QUEUE        *elem;
+  T_TX_QUEUE        **insert;
+
+  static ULONG allocation_number = 0;
+
+  TRACE_FUNCTION ("tx_reserve_buffer");
+
+  MALLOC (elem, sizeof(T_TX_QUEUE));
+  
+  /*
+   * Increase allocation number. Use of reservation_no must correspond 
+   * to use in tx_store_buffer().
+   */
+  *reservation_no = ++(allocation_number);
+
+  /*
+   * Store all required information in new queue element. Member primitive 
+   * is set to NULL to indicate that the element is not yet ready to be sent.
+   * Copy necessary primitive header information to ph_* variables.
+   */
+  elem->primitive       = (ULONG)NULL;
+  elem->prim_type       = prim_type;
+  elem->reference       = allocation_number;
+  elem->rx_service      = rx_service;
+  elem->remove_frame    = FALSE;
+  elem->ph_sapi         = ll_unitdesc_req->sapi;  
+  elem->ph_tlli         = ll_unitdesc_req->tlli;
+  elem->ph_grlc_qos_peak = ll_unitdesc_req->ll_qos.peak;
+  elem->ph_radio_prio   = ll_unitdesc_req->radio_prio;
+  elem->ph_cause        = cause;
+#ifdef REL99 
+  elem->ph_pkt_flow_id  = (UBYTE)ll_unitdesc_req->pkt_flow_id;
+#endif /* REL99 */
+
+  if ((cause EQ GRLC_DTACS_DEF) OR (cause EQ GRLC_DTACS_MOBILITY_MANAGEMENT))
+  {
+    /*
+     * "Normal" frame cause, append element at the end of queue. Let insert
+     * either point to llc_data->tx.queue or to the member .next of an element.
+     */
+    insert = &llc_data->tx.queue;
+    while (*insert NEQ NULL)
+    {
+      insert = &((*insert)->next);
+    }
+
+    /*
+     * Insert new element at found location and mark element as last in queue.
+     */
+    *insert = elem;
+    elem->next = NULL;
+  }
+  else /* GRLC_DTACS_PAGE_RESPONSE OR GRLC_DTACS_CELL_UPDATE */
+  {
+    /*
+     * No "normal" frame cause, thus LLGMM_TRIGGER_REQ must have been
+     * received. Insert element at the beginning of queue.
+     */
+    elem->next = llc_data->tx.queue;
+    llc_data->tx.queue = elem;
+  }
+
+  /*
+   * Check if a service for flow control is specified (currently only 
+   * SERVICE_UITX and SERVICE_ITX are recognized).
+   */
+  *buffer_available = FALSE;
+
+  if (rx_service EQ SERVICE_UITX)
+  {
+    /*
+     * Decrement the space counter of UITX primitives in the queue for the
+     * current SAPI. This means that one place of the queue is occupied.
+     * Check if there is space for one more primitive in the queue.
+     */
+    int n = llc_data->tx.queue_counter_uitx[UIMAP(llc_data->current_sapi)];
+    
+    if (n > 0)
+    {
+      llc_data->tx.queue_counter_uitx[UIMAP(llc_data->current_sapi)]--;
+
+      if (n > 1) 
+      {
+        *buffer_available = TRUE;
+      }
+    }
+    else
+    {
+      TRACE_EVENT ("Check uitx-queue flow");
+    }
+  }
+  else if (rx_service EQ SERVICE_ITX)
+  {
+    /*
+     * Decrement the space counter of ITX primitives in the queue for the
+     * current SAPI. This means that one place of the queue is occupied.
+     * Check if there is space for one more primitive in the queue.
+     */
+    int n = llc_data->tx.queue_counter_itx[IMAP(llc_data->current_sapi)];
+
+    if (n > 0)
+    {
+      llc_data->tx.queue_counter_itx[IMAP(llc_data->current_sapi)]--;
+
+      if (n > 1)
+      {
+        *buffer_available = TRUE;
+      }
+    }
+    else
+    {
+      TRACE_EVENT ("Check itx-queue flow");
+    }
+  }
+  else
+  {
+    /*
+     * All other services (e.g. SERVICE_U) are allowed to send primitives 
+     * per default. I.E. no flow control, except for service UITX and ITX.
+     */
+    *buffer_available = TRUE;
+  }
+} /* tx_reserve_buffer() */
+
+
+/*
++------------------------------------------------------------------------------
+| Function    : tx_store_buffer
++------------------------------------------------------------------------------
+| Description : This procedure stores the given CCI primitive as GRLC primitive 
+|               in the already reserved local transmit queue element that is 
+|               given with primitive parameter reference. This queue element 
+|               is marked as 'ready to send'. The CCI primitive is being 
+|               PPASSED as GRLC_DATA_REQ/GRLC_UNITDATA_REQ, according to 
+|               prim_type in the queue element. All header information for 
+|               the GRLC primitive is being filled in from the already stored 
+|               data in the element.
+|
+| Parameters  : cci_cipher_cnf - a valid pointer to a CCI_CIPHER_CNF primitive
+|
++------------------------------------------------------------------------------
+*/
+GLOBAL void tx_store_buffer (T_GRLC_DATA_REQ *grlc_data_req)
+{
+  T_TX_QUEUE        *elem = llc_data->tx.queue;
+
+
+  TRACE_FUNCTION ("tx_store_buffer");
+
+  /*
+   * Find corresponding queue entry. Use of reference must correspond to
+   * use in tx_reserve_buffer().
+   */
+  while (elem NEQ NULL)
+  {
+    if (elem->reference EQ grlc_data_req->tlli)
+    {
+      break;
+    }
+
+    elem = elem->next;
+  }
+  
+  if (elem NEQ NULL)
+  {
+    if (elem->prim_type EQ PRIM_DATA)
+    {
+      /*
+       * Copy information from stored primitive header variables (ph_*) to 
+       * GRLC primitive.
+       */
+      grlc_data_req->sapi              = elem->ph_sapi;
+      grlc_data_req->tlli              = elem->ph_tlli;
+#ifdef LL_2to1
+      grlc_data_req->grlc_qos.peak      = elem->ph_grlc_qos_peak;
+#else
+      grlc_data_req->grlc_qos.peak      = elem->ph_grlc_qos_peak;
+#endif
+      grlc_data_req->radio_prio        = elem->ph_radio_prio;
+      grlc_data_req->cause             = elem->ph_cause;
+
+#ifdef REL99 
+      grlc_data_req->pkt_flow_id[0]    = elem->ph_pkt_flow_id;
+#endif /* REL99*/
+
+#ifdef _SIMULATION_
+      /*
+       * Initialize all (unused) members of grlc_qos, because otherwise the test
+       * cases fail.
+       */
+#ifdef LL_2to1
+      grlc_data_req->grlc_qos.delay     = PS_DELAY_SUB;
+      grlc_data_req->grlc_qos.relclass  = PS_RELCLASS_SUB;
+      grlc_data_req->grlc_qos.preced    = PS_PRECED_SUB;
+      grlc_data_req->grlc_qos.mean      = PS_MEAN_SUB;
+#else
+      grlc_data_req->grlc_qos.delay     = GRLC_DELAY_SUB;
+      grlc_data_req->grlc_qos.relclass  = GRLC_RELCLASS_SUB;
+      grlc_data_req->grlc_qos.preced    = GRLC_PRECED_SUB;
+      grlc_data_req->grlc_qos.mean      = GRLC_MEAN_SUB;
+#endif
+      
+#endif /* _SIMULATION_ */
+
+      elem->primitive = (ULONG)grlc_data_req;
+    }
+    else /* PRIM_UNITDATA */
+    {
+      /*
+       * Store CCI primitive as GRLC_UNITDATA_REQ (and mark it as stored).
+       */
+      PPASS (grlc_data_req, grlc_unitdata_req, GRLC_UNITDATA_REQ);
+
+      /*
+       * Copy information from stored primitive header variables (ph_*) to 
+       * GRLC primitive (omit ph_cause, because it is not present in 
+       * GRLC_UNITDATA_REQ).
+       */
+      grlc_unitdata_req->sapi              = elem->ph_sapi;
+      grlc_unitdata_req->tlli              = elem->ph_tlli;
+#ifdef LL_2to1
+      grlc_unitdata_req->grlc_qos.peak = elem->ph_grlc_qos_peak;
+#else
+      grlc_unitdata_req->grlc_qos.peak      = elem->ph_grlc_qos_peak;
+#endif
+      grlc_unitdata_req->radio_prio        = elem->ph_radio_prio;
+#ifdef REL99 
+      grlc_unitdata_req->pkt_flow_id[0]    = elem->ph_pkt_flow_id;
+#endif /* REL99*/
+#ifdef _SIMULATION_
+      /*
+       * Initialize all (unused) members of grlc_qos, because otherwise the test
+       * cases fail.
+       */
+#ifdef LL_2to1
+      grlc_unitdata_req->grlc_qos.delay = PS_DELAY_SUB;
+      grlc_unitdata_req->grlc_qos.relclass  = PS_RELCLASS_SUB;
+      grlc_unitdata_req->grlc_qos.preced    = PS_PRECED_SUB;
+      grlc_unitdata_req->grlc_qos.mean      = PS_MEAN_SUB;
+#else
+      grlc_unitdata_req->grlc_qos.delay     = GRLC_DELAY_SUB;
+      grlc_unitdata_req->grlc_qos.relclass  = GRLC_RELCLASS_SUB;
+      grlc_unitdata_req->grlc_qos.preced    = GRLC_PRECED_SUB;
+      grlc_unitdata_req->grlc_qos.mean      = GRLC_MEAN_SUB;
+#endif /* LL_2to1 */
+
+
+
+
+#endif /* _SIMULATION_ */
+
+      elem->primitive = (ULONG)grlc_unitdata_req;
+    }
+  }
+  else /* elem == NULL */
+  {
+    /*
+     * elem not found is possible in case of an LLC re-init, reset, 
+     * unassign, ...
+     */
+    TRACE_0_INFO("No TX queue entry for given reference found");
+    PFREE (grlc_data_req);
+  }
+
+} /* tx_store_buffer() */
+
+
+/*
++------------------------------------------------------------------------------
+| Function    : tx_get_next_frame
++------------------------------------------------------------------------------
+| Description : When LLC is not suspended, this procedure gets (and removes) 
+|               the first primitive out of the local transmit queue and stores 
+|               it in one of the parameters, according to the type of the 
+|               primitive (which is written in prim_type). If no frame is 
+|               available in the queue (i.e. first queue element is not marked
+|               as 'ready to send', or the queue is empty), the value NO_PRIM
+|               is written in prim_type. The parameter rx_service indicates the
+|               originator service. In case the rx_service is UITX or ITX an 
+|               READY signal for flow control will be send after frame 
+|               transmission.
+|               When LLC is suspended, this procedure returns the first 
+|               primitive for SAPI 1 or the first U frame for any SAPI, which 
+|               is ready to send.
+|
+| Parameters  : grlc_data_req - a valid pointer to a pointer to a GRLC-DATA-REQ 
+|                              primitive
+|               grlc_unitdata_req - a valid pointer to a pointer to a 
+|                                  GRLC-UNITDATA-REQ primitive
+|               prim_type - will be set to PRIM_DATA or PRIM_UNITDATA, must be
+|                           a valid pointer
+|               rx_service - indicates the originater service 
+|                            must be a valid pointer
+|
++------------------------------------------------------------------------------
+*/
+GLOBAL void tx_get_next_frame (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)
+{ 
+  int               frame_len;
+  int               ctrl_len;
+  T_TX_QUEUE        *elem;
+  T_TX_QUEUE        **find;
+
+
+  TRACE_FUNCTION( "tx_get_next_frame" );
+
+
+  /*
+   * Initialise find w/ queue start. find points always to the element 
+   * "before" the examined one, to be able to modifiy the queue, if
+   * an element is found.
+   */
+  find = &llc_data->tx.queue;
+
+  /*
+   * If LLC is suspended search the queue for the first GRLC_DATA_REQ primitive,
+   * otherwise take the first primitive in queue (if any).
+   */
+  if (llc_data->suspended EQ TRUE)
+  {
+    /*
+     * LLC is in suspended mode. Search the queue for the first primitive 
+     * for SAPI 1 or the first U frame for any SAPI
+     */
+    while ( (*find              NEQ NULL)       AND 
+           ((*find)->ph_sapi    NEQ LL_SAPI_1)  AND 
+           ((*find)->rx_service NEQ SERVICE_U)  AND
+           ((*find)->primitive  NEQ (ULONG)NULL)  )
+    {
+      find = &((*find)->next);
+    }
+  }
+
+  /*
+   * Let elem point to queue element (NULL, if no element present/found).
+   */
+  elem = *find;
+
+
+  /*
+   * Check if queue is empty or "first" queue element (depending on suspended
+   * mode) is marked as not 'ready to send', i.e. it contains a NULL 
+   * primitive pointer.
+   */
+  if ((elem EQ NULL) OR (elem->primitive EQ (ULONG)NULL))
+  {
+    /*
+     * Queue is empty or "first" element is not yet 'ready to send'.
+     */
+    *prim_type = NO_PRIM;
+  }
+  else /* (elem NEQ NULL) AND (elem->primitive NEQ NULL) */
+  {
+    /*
+     * Found a valid (and 'ready to send') element in queue.
+     */
+
+    /*
+     * Remove found element from queue (set member next of element before
+     * the found element to the element behind the found element and thus
+     * skip the found element).
+     */
+    *find = (*find)->next;
+  
+    /*
+     * Store data of element in the given parameters.
+     */
+    *prim_type  = elem->prim_type;
+    *rx_service = elem->rx_service;
+    *sapi       = elem->ph_sapi;
+
+    if (elem->prim_type EQ PRIM_DATA)
+    {
+      *grlc_data_req = (T_GRLC_DATA_REQ *)elem->primitive;
+
+      frame_len = BYTELEN((*grlc_data_req)->sdu.l_buf);
+    }
+    else /* PRIM_UNITDATA */
+    {
+      *grlc_unitdata_req = (T_GRLC_UNITDATA_REQ *)elem->primitive;
+
+      frame_len = BYTELEN((*grlc_unitdata_req)->sdu.l_buf);
+    }
+
+    /*
+     * If the information fild doesn't fit in current N201_U, remove frame
+     * (only applies to service U and UITX).
+     */
+    if (*rx_service == SERVICE_U || *rx_service == SERVICE_UITX)
+    {
+      ctrl_len = (*rx_service == SERVICE_U) ? U_FRAME_MIN_OCTETS
+                                            : UI_FRAME_MIN_OCTETS;
+
+      if (frame_len > llc_data->n201_u_base[UIMAP(*sapi)] + ctrl_len)
+      {
+        elem->remove_frame = TRUE;
+        TRACE_0_INFO("Primitive in TX exceeds N201-U: marked to remove");
+      }
+    }
+
+    /*
+     * If frame is marked to remove, do it here
+     */
+    if (elem->remove_frame == TRUE)
+    {
+      if (elem->prim_type EQ PRIM_DATA)
+      {
+        PFREE (*grlc_data_req);
+        TRACE_0_INFO("GRLC_DATA_REQ removed from TX queue");
+      }
+      else
+      {
+        PFREE (*grlc_unitdata_req);
+        TRACE_0_INFO("GRLC_UNITDATA_REQ removed from TX queue");
+      }
+
+      *prim_type  = PRIM_REMOVED;
+    }
+
+    /*
+     * Remove TX queue entry management element
+     */
+    MFREE (elem);
+
+    /*
+     * Check if service queue space should be tracked (at the moment only 
+     * for service UITX and ITX, due to flow control to this service).
+     */
+    if (*rx_service EQ SERVICE_UITX)
+    {
+      /*
+       * Increment the UITX space counter for the SAPI of the primitive
+       * to indicate that the primitive has been stored in queue.
+       */
+      if (*prim_type EQ PRIM_DATA)
+      {
+        llc_data->tx.queue_counter_uitx[UIMAP(*sapi)]++;
+      }
+      else /* PRIM_UNITDATA */
+      {
+        llc_data->tx.queue_counter_uitx[UIMAP(*sapi)]++;
+      }
+    }
+    else if (*rx_service EQ SERVICE_ITX)
+    {
+      /*
+       * Increment the ITX space counter for the SAPI of the primitive
+       * to indicate that the primitive has been stored in queue.
+       */
+      llc_data->tx.queue_counter_itx[IMAP(*sapi)]++;
+    }
+  }
+
+  return;
+} /* tx_get_next_frame() */
+
+
+/*
++------------------------------------------------------------------------------
+| Function    : tx_get_first_data_frame
++------------------------------------------------------------------------------
+| Description : This procedure finds the first GRLC_DATA_REQ primitive in the 
+|               local transmit queue and stores its queue element in the 
+|               parameter element, if any. The primitive (element) stays in 
+|               the queue. If no GRLC_DATA_REQ primitive is found in the queue, 
+|               no element is stored in element, but the value NULL.
+|
+| Parameters  : elem          - must be a valid pointer to a pointer to a 
+|                               TX queue element
+|
++------------------------------------------------------------------------------
+*/
+GLOBAL void tx_get_first_data_frame (T_TX_QUEUE **elem)
+{
+  TRACE_FUNCTION( "tx_get_first_data_frame" );
+
+  /*
+   * Check the local transmit queue until a GRLC_DATA_REQ is found or the end
+   * of the queue has been reached. In the latter case, *elem is automatically
+   * set to NULL.
+   */
+  for (*elem = llc_data->tx.queue; *elem NEQ NULL; *elem = (*elem)->next)
+  {
+    /*
+     * Check if primitive is a GRLC_DATA_REQ primitive.
+     */
+    if ((*elem)->prim_type EQ PRIM_DATA)
+    {
+      break;
+    }
+  }
+
+  return;
+} /* tx_get_first_data_frame() */
+
+
+/*
++------------------------------------------------------------------------------
+| Function    : tx_remove_data_frames
++------------------------------------------------------------------------------
+| Description : This procedure removes all PRIM_DATA for given SAPI and service
+|               from TX queue. If an element is removed and 
+|
+| Parameters  : service - service which requests to delete its primitives
+|               sapi    - the sapi of the primitives to delete
+|
++------------------------------------------------------------------------------
+*/
+GLOBAL void tx_remove_data_frames (T_SERVICE service, T_SAPI sapi)
+{
+  T_TX_QUEUE   *elem;
+  T_TX_QUEUE  **find = &llc_data->tx.queue;
+  BOOL          elem_removed = FALSE;
+
+  TRACE_FUNCTION( "tx_remove_data_frames" );
+
+  while (*find NEQ NULL)
+  {
+    /*
+     * Let elem point to queue element
+     */
+    elem = *find;
+
+    /*
+     * Check if primitive is a GRLC_DATA_REQ primitive and
+     * check the sapi of the queue element
+     */
+    if ((elem->prim_type  EQ PRIM_DATA) AND
+        (elem->ph_sapi    EQ sapi)      AND
+        (elem->rx_service EQ service)     )
+    {
+      elem_removed = TRUE;
+
+      /*
+       * Remove found element from queue (set member next of element before
+       * the found element to the element behind the found element and thus
+       * skip the found element).
+       */
+      *find = (*find)->next;
+
+      /*
+       * Check if we have a valid pointer to a primitive
+       */
+      if ((elem->primitive NEQ (ULONG)NULL))
+      {
+        PFREE ((T_GRLC_DATA_REQ *)elem->primitive);
+
+        TRACE_0_INFO("TX queue element and primitive removed");
+      }
+      else
+      {
+        /*
+         * Primitive is in use at CCI.
+         * It is no problem if the corresponding primitive to this entry 
+         * returns from CCI, because the reference number will not be found 
+         * and the primitive data will then be freed.
+         */
+        TRACE_0_INFO("In use TX queue element removed");
+      }
+
+      /*
+       * Remove TX queue entry management element.
+       */
+      MFREE (elem);
+
+      /*
+       * Check if service queue space should be tracked (at the moment only 
+       * for service UITX and ITX, due to flow control to this service).
+       * In this case increment the space counter for the SAPI of the 
+       * primitive to indicate that the primitive has been stored in queue.
+       */
+      if (service EQ SERVICE_UITX)
+      {
+        llc_data->tx.queue_counter_uitx[UIMAP(sapi)]++;
+      }
+      else if (service EQ SERVICE_ITX)
+      {
+        llc_data->tx.queue_counter_itx[IMAP(sapi)]++;
+      }
+    }
+    else
+    {
+      /*
+       * Point to the next queue element pointer
+       */
+      find = &((*find)->next);
+    }
+  }
+
+  /*
+   * Now check, if a ready indication must be send must be send to the 
+   * requesting service (if at least one elem was removed, send one).
+   * Currently only service UITX and ITX are requesting a signal.
+   */
+  if (elem_removed)
+  {
+    SWITCH_LLC (sapi);
+
+    if (service EQ SERVICE_UITX)
+    {
+      sig_tx_uitx_ready_ind();
+    }
+    else if (service EQ SERVICE_ITX)
+    {
+      sig_tx_itx_ready_ind();
+    }
+  }
+
+  return;
+} /* tx_remove_data_frames() */
+