view src/g23m-gprs/grlc/grlc_ruf.c @ 303:f76436d19a7a default tip

!GPRS config: fix long-standing AT+COPS chance hanging bug There has been a long-standing bug in FreeCalypso going back years: sometimes in the AT command bring-up sequence of an ACI-only MS, the AT+COPS command would produce only a power scan followed by cessation of protocol stack activity (only L1 ADC traces), instead of the expected network search sequence. This behaviour was seen in different FC firmware versions going back to Citrine, and seemed to follow some law of chance, not reliably repeatable. This bug has been tracked down and found to be specific to !GPRS configuration, stemming from our TCS2/TCS3 hybrid and reconstruction of !GPRS support that was bitrotten in TCS3.2/LoCosto version. ACI module psa_mms.c, needed only for !GPRS, was missing in the TCS3 version and had to be pulled from TCS2 - but as it turns out, there is a new field in the MMR_REG_REQ primitive that needs to be set correctly, and that psa_mms.c module is the place where this initialization needed to be added.
author Mychaela Falconia <falcon@freecalypso.org>
date Thu, 08 Jun 2023 08:23:37 +0000
parents fa8dc04885d8
children
line wrap: on
line source

/* 
+----------------------------------------------------------------------------- 
|  Project :  GPRS (8441)
|  Modul   :  GRLC
+----------------------------------------------------------------------------- 
|  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 module implements local functions for service RU of
|             entity GRLC.
+----------------------------------------------------------------------------- 
*/ 

#ifndef GRLC_RUF_C
#define GRLC_RUF_C
#endif

#define ENTITY_GRLC


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

#include <stdio.h>
#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 "ccdapi.h"     /* to get CCD API */
#include "cnf_grlc.h"    /* to get cnf-definitions */
#include "mon_grlc.h"    /* to get mon-definitions */
#include "prim.h"       /* to get the definitions of used SAP and directions */
#include "message.h"
#include "grlc.h"        /* to get the global entity definitions */
#include "grlc_f.h"
#include "grlc_tms.h"
#include "grlc_rds.h"
#include "grlc_ruf.h"
#include <string.h>
#include "cl_rlcmac.h"

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

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

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

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



/*
+------------------------------------------------------------------------------
| Function    : ru_init
+------------------------------------------------------------------------------
| Description : The function ru_init() set init values in null state.
|
| Parameters  : dummy - description of parameter dummy
|
+------------------------------------------------------------------------------
*/
GLOBAL void ru_init ( void )
{ 
   UBYTE i;
   UBYTE j;
  
  TRACE_FUNCTION( "ru_init" );
  INIT_STATE(RU,RU_NULL);

  /*
   * UBYTE
   */  
  grlc_data->ru.nts               = 0xFF;
  grlc_data->ru.nts_max           = 0xFF;
  grlc_data->ru.ti                = 0xFF;
  grlc_data->ru.next_prim         = 0xFF;
  grlc_data->ru.active_prim       = 0xFF;
  grlc_data->ru.vs                = 0xFF;
  grlc_data->ru.va                = 0xFF;
  grlc_data->ru.bsn_ret           = 0xFF;
  grlc_data->ru.last_si_block     = 0xFF;
  grlc_data->ru.cv                = 0xFF;
  grlc_data->ru.N3104             = 0xFF;
  grlc_data->ru.N3104_MAX         = 0xFF;
  grlc_data->ru.block_status      = 0xFF;
  grlc_data->ru.poll_tn           = 0xFF;
  grlc_data->ru.count_cv_0        = 0xFF;
  grlc_data->ru.nr_nacked_blks    = 0xFF;
  grlc_data->ru.pdu_cnt           = 0xFF; 
  grlc_data->ru.pdu_sent          = 0xFF; 
  grlc_data->ru.pdu_rem           = 0xFF; 
  grlc_data->ru.pdu_boundaries    = 0xFF; 

  grlc_data->ru.write_pos_index   = 0xFF;
  
  /*
   * USHORT
   */
  grlc_data->ru.rlc_data_size     = 0xFFFF;
  grlc_data->ru.sdu_len           = 0xFFFF;
  grlc_data->ru.sdu_off           = 0xFFFF;
  grlc_data->ru.rlc_octet_cnt     = 0xFFFF;
  grlc_data->ru.tbc               = 0xFFFF;  
  grlc_data->ru.cnt_ts            = 0xFFFF;
  
  /*
   * BOOL
   */
  grlc_data->ru.tlli_cs_type      = FALSE;
  grlc_data->ru.cd_active         = TRUE;
  grlc_data->ru.first_usf         = FALSE;
  grlc_data->ru.release_tbf       = FALSE;
#if defined REL99 AND defined TI_PS_FF_TBF_EST_PACCH
  grlc_data->ru.tbf_re_est        = FALSE;
#endif

  /*
   * Type
   */  
  grlc_data->ru.cs_type           = CS_ZERO;
  /*
   * structs
   */
  grlc_data->ru.pl_retrans_current.cnt = 0xFF;
  memset(grlc_data->ru.pl_retrans_current.blk, 0xFF, 4);
  /*
   * array
   */
  for (i=0;i<WIN_SIZE;i++)
    grlc_data->ru.vb[i & WIN_MOD] = VB_INVALID;
  /* 
   * global structs
   */
  for (i=0;i<NEXT_POLL_ARRAY_SIZE;i++)
  { 
    /*
     * set free list
     */
    grlc_data->next_poll_array[i].next      = i+1;
    grlc_data->next_poll_array[i].fn        = 0xFFFFFFFF;
    grlc_data->next_poll_array[i].cnt       = 0;
    for(j=0; j< POLL_TYPE_ARRAY_SIZE; j++)
      grlc_data->next_poll_array[i].poll_type[j] = CGRLC_POLL_NONE;
  }
  /* 
   * last free entry points to 0xff
   */ 
  grlc_data->next_poll_array[NEXT_POLL_ARRAY_SIZE-1].next = 0xFF;  
  /* 
   * indicates invalid paramters
   */
  grlc_data->poll_start_free = 0;
  grlc_data->poll_start_tbf  = 0xFF;   

} /* ru_init() */



/*

+------------------------------------------------------------------------------
| Function    : ru_within_window
+------------------------------------------------------------------------------
| Description : The function ru_within_window() checks if the bsn_i value is 
|               between high_value_i and low_value_i(modulo operating).  
|               The return value is result.
|               
|
| Parameters  : bsn_i          - chech this value
|               high_value_i   - right side of the window
|               low_value_i    - left side of the window
|
+------------------------------------------------------------------------------
*/
GLOBAL BOOL ru_within_window ( UBYTE bsn_i, UBYTE high_value_i,UBYTE low_value_i )
{ 
  BOOL result = FALSE;
  TRACE_FUNCTION( "ru_within_window" );
  
  if(
     ( high_value_i >= low_value_i  ) AND  /*   vs >= va  */
     ( bsn_i        >  low_value_i  ) AND  /*  ssn >  va  */
     ( bsn_i        <= high_value_i )      /*   vs >= ssn */
    )
  {
    result = TRUE;
  }
  else if(  ( high_value_i <  low_value_i  )   /*  va > vs  */
              AND
            (   (  ( bsn_i   > low_value_i  ) AND     /*  ssn > va  */
                   ( bsn_i   > high_value_i ))    OR  /*  ssn > vs  */   
                (  ( bsn_i   < low_value_i  ) AND     /*  ssn > va  */
                   ( bsn_i  <= high_value_i ))        /*  ssn <= vs  */
            )
    )
  {
    result = TRUE;
  }

 /* if(!result)
  {
    TRACE_EVENT_P3("BSN not in range: low=%d bsn=%d high=%d", low_value_i,bsn_i,high_value_i);
  }
 */

  return result;
} /* ru_within_window() */



/*
+------------------------------------------------------------------------------
| Function    : ru_recalc_rlc_oct_cnt
+------------------------------------------------------------------------------
| Description : The function ru_recalc_rlc_oct_cnt() the number of octetcs
|               during the TBF. It is needed for starting the countdown procedure 
|
| Parameters  : dummy - description of parameter dummy
|
+------------------------------------------------------------------------------
*/
GLOBAL USHORT ru_recalc_rlc_oct_cnt ( void )
{ 
  USHORT  result;
  UBYTE   next_cnt;
  UBYTE   prev_cnt;
  UBYTE   rem_pdu_bound;
  ULONG   rest_of_pdu;
  UBYTE   last_pdu_bound;
  TRACE_FUNCTION( "ru_recalc_rlc_oct_cnt" );

  grlc_data->ru.next_prim = grlc_data->prim_queue[grlc_data->ru.active_prim].next;
  result                = grlc_data->ru.sdu_len;
  next_cnt              = grlc_data->ru.next_prim;
  if(grlc_data->ru.sdu_len)
    grlc_data->ru.pdu_rem  = 1;
  
  /*init calculate nr of pdu boundaries*/
  rest_of_pdu = grlc_data->ru.sdu_len%grlc_data->ru.rlc_data_size;
  if((rest_of_pdu EQ 0)                                  AND 
     ((next_cnt EQ 0xFF)                             OR      /* either no primitive in queue         */
       grlc_data->prim_queue[next_cnt].start_new_tbf))       /* or next primitive belongs to new tbf */
    rem_pdu_bound = 0; /*fills excatly in cv=0*/
  else if(rest_of_pdu EQ 0)
    rem_pdu_bound = 2; /*fills exxalty, more pdus rerq, therefore 2 bounds*/
  else
    rem_pdu_bound = 1; /*normal case*/
  last_pdu_bound = rem_pdu_bound;
  
#ifdef _SIMULATION_
  TRACE_EVENT_P4("pdu_len=%d,rest_of_pdu=%d,rem_pdu_bound=%d,last_pdu_bound=%d",
                                grlc_data->ru.sdu_len,
                                rest_of_pdu,
                                rem_pdu_bound,
                                last_pdu_bound );
#endif /* #ifdef _SIMULATION_ */

  prev_cnt = grlc_data->ru.active_prim;
  while((next_cnt < PRIM_QUEUE_SIZE_TOTAL) AND 
	    (grlc_data->prim_queue[next_cnt].start_new_tbf EQ 0)
         )
  {
    ULONG sdu_len;
    result   += grlc_data->prim_queue[next_cnt].prim_ptr->sdu.l_buf/8;
    sdu_len   = grlc_data->prim_queue[next_cnt].prim_ptr->sdu.l_buf/8;
    grlc_data->prim_queue[next_cnt].previous   = prev_cnt;
    grlc_data->prim_queue[next_cnt].cv_status  = FALSE;
    grlc_data->prim_queue[next_cnt].rlc_status = FALSE;
    grlc_data->prim_queue[next_cnt].last_bsn   = 0xFF;
 
    prev_cnt  = next_cnt;
    next_cnt  = grlc_data->prim_queue[next_cnt].next;
    grlc_data->ru.pdu_rem++;
    
    /*init calculate nr of pdu boundaries*/
    
    rest_of_pdu = ((sdu_len+last_pdu_bound+rest_of_pdu)%grlc_data->ru.rlc_data_size);
    if((rest_of_pdu EQ 0) AND ((next_cnt EQ 0xFF) OR grlc_data->prim_queue[next_cnt].start_new_tbf))
    {
      rem_pdu_bound += 0; /*fills excatly in cv=0*/
      last_pdu_bound = 0;
    }
    else if(rest_of_pdu EQ 0)
    {
      rem_pdu_bound += 2; /*fills exxalty, more pdus rerq, therefore 2 bounds*/
      last_pdu_bound = 2;
    }
    else
    {
      rem_pdu_bound += 1; /*normal case*/
      last_pdu_bound = 1;
    }    
#ifdef _SIMULATION_
  TRACE_EVENT_P4("pdu_len=%d,rest_of_pdu=%d,rem_pdu_bound=%d,last_pdu_bound=%d",
                                                                                sdu_len,
                                                                                rest_of_pdu,
                                                                                rem_pdu_bound,
                                                                                last_pdu_bound );
#endif /* #ifdef _SIMULATION_ */

  }
  grlc_data->ru.pdu_cnt         = grlc_data->ru.pdu_rem+grlc_data->ru.pdu_sent;
  grlc_data->ru.pdu_boundaries  = rem_pdu_bound;
#ifdef _SIMULATION_
  TRACE_EVENT_P4("pdu_cnt=%d,pdu_sent=%d,pdu_rem=%d,needed pdu_boundaries=%d",
                                                                            grlc_data->ru.pdu_cnt,
                                                                            grlc_data->ru.pdu_sent,
                                                                            grlc_data->ru.pdu_rem,
                                                                            grlc_data->ru.pdu_boundaries );
#endif /* #ifdef _SIMULATION_ */
    
  grlc_data->tbf_ctrl[grlc_data->ul_index].pdu_cnt     = grlc_data->ru.pdu_cnt;
  return result;
} /* ru_recalc_rlc_oct_cnt() */





/*
+------------------------------------------------------------------------------
| Function    : ru_get_next_sdu
+------------------------------------------------------------------------------
| Description : The function ru_get_next_sdu() gets the next GRLC_DATA_REQ from
|               the queue.  
|
| Parameters  : dummy - description of parameter dummy
|
+------------------------------------------------------------------------------
*/
GLOBAL void ru_get_next_sdu ( void )
{ 
  TRACE_FUNCTION( "ru_get_next_sdu" );

  grlc_data->ru.pdu_rem--;
  grlc_data->ru.pdu_sent++;
  grlc_data->prim_queue[grlc_data->ru.active_prim].last_bsn = grlc_data->ru.vs;

  if((grlc_data->prim_queue[grlc_data->ru.next_prim].start_new_tbf EQ 0) AND 
      grlc_data->ru.next_prim NEQ 0xFF)
  {
    grlc_data->ru.active_prim = grlc_data->prim_queue[grlc_data->ru.active_prim].next;
    grlc_data->ru.sdu_len    = grlc_data->prim_queue[grlc_data->ru.active_prim].prim_ptr->sdu.l_buf/8;
    grlc_data->ru.sdu_off    = grlc_data->prim_queue[grlc_data->ru.active_prim].prim_ptr->sdu.o_buf/8;
    grlc_data->prim_queue[grlc_data->ru.active_prim].rlc_status = TRUE;
    grlc_data->ru.next_prim = grlc_data->prim_queue[grlc_data->ru.active_prim].next;    
  }
  else
  {
    grlc_data->ru.sdu_len    = 0;
    grlc_data->ru.sdu_off    = 0;

  }

#ifdef _SIMULATION_
  TRACE_EVENT_P3("pdu_cnt=%d,pdu_sent=%d,pdu_rem=%d",
                                                    grlc_data->ru.pdu_cnt,
                                                    grlc_data->ru.pdu_sent,
                                                    grlc_data->ru.pdu_rem );
#endif /* #ifdef _SIMULATION_ */
 
} /* ru_get_next_sdu() */





/*
+------------------------------------------------------------------------------
| Function    : ru_calc_rlc_data_size
+------------------------------------------------------------------------------
| Description : The function ru_calc_rlc_data_size() calculates the Data size of
|               an RLC data block without header and TLLI. The size is described
|               in bytes.
|
| Parameters  : cs_type_i - Coding scheme, determines the size of an RLC data block
|                 
+------------------------------------------------------------------------------
*/
GLOBAL USHORT ru_calc_rlc_data_size ( T_CODING_SCHEME  cs_type_i, UBYTE ti_bit_i )
{ 
  USHORT  result=0;
  UBYTE   tlli_size;
  TRACE_FUNCTION( "ru_calc_rlc_data_size" );
  
  /*
   * check if tlli must be included in RLC data blocks
   */
  if (ti_bit_i EQ 0)
    tlli_size = 0; 
  else
  {
    tlli_size = TLLI_SIZE; 
    if(!(grlc_data->ru.tlli_cs_type))
      cs_type_i = CS_1; 
#ifdef REL99  
    if(grlc_data->pfi_support)
    {
      tlli_size++;
    } 
#endif
  }
  
  switch( cs_type_i)
  {
  case CS_1:
    result = 20;
    break;
  case CS_2:
    result = 30;
    break;
  case CS_3:
    result = 36;
    break;
  case CS_4:
    result = 50;
    break;
  default:
  if (!result)
  {
    TRACE_EVENT_P2("Unknown coding scheme, tsize=%d, t_cs=%d", 
                     tlli_size, grlc_data->ru.tlli_cs_type);
    result = 20; /* Going by CS1 coding scheme by default */
  }
    
    break;
  } 

  
  result -= tlli_size;
  
  return result;
} /* ru_calc_rlc_data_size() */


/*
+------------------------------------------------------------------------------
| Function    : ru_set_block_status
+------------------------------------------------------------------------------
| Description : The function ru_set_block_status() sets the block status.
|
| Parameters  : cs_type_i - Coding scheme, determines the block status
|                 
+------------------------------------------------------------------------------
*/
GLOBAL USHORT ru_set_block_status ( T_CODING_SCHEME  cs_type_i )
{ 
  USHORT  result=0;
  TRACE_FUNCTION( "ru_set_block_status" );

  if(!(grlc_data->ru.tlli_cs_type) AND 
     grlc_data->ru.ti)
     cs_type_i = CS_1; 

  switch( cs_type_i)
  {
  case CS_1:
    result = 2;
    break;
  case CS_2:
    result = 4;
    break;
  case CS_3:
    result = 5;
    break;
  case CS_4:
    result = 6;
    break;
  default:
    TRACE_EVENT_P3("Unknown CS cstype=%d t_cs=%d, ti=%d",cs_type_i,
                    grlc_data->ru.tlli_cs_type, grlc_data->ru.ti);
    
    break;
  } 
  return result;
} /* ru_set_block_status() */


/*
+------------------------------------------------------------------------------
| Function    : ru_get_cs_type
+------------------------------------------------------------------------------
| Description : The function ru_get_cs_type() returns the coding scheme 
|               for the requeseted block status.
|
| Parameters  : bs_i - requested block status
|                 
+------------------------------------------------------------------------------
*/
GLOBAL T_CODING_SCHEME  ru_get_cs_type ( USHORT  bs_i )
{ 
  T_CODING_SCHEME  result= CS_ZERO;
    ;
  TRACE_FUNCTION( "ru_get_cs_type" );
  

  switch( bs_i)
  {
  case 2:
    result = CS_1;
    break;
  case 4:
    result = CS_2;
    break;
  case 5:
    result = CS_3;
    break;
  case 6:
    result = CS_4;
    break;
  default:
    TRACE_ERROR("unknown block status");
    break;
  } 
  return result;
} /* ru_get_cs_type() */  
/*
+------------------------------------------------------------------------------
| Function    : ru_tbf_init
+------------------------------------------------------------------------------
| Description : The function ru_tbf_init() sets the parameters at tbf assignment
|
| Parameters  : dummy - description of parameter dummy
|
+------------------------------------------------------------------------------
*/
GLOBAL void ru_tbf_init ( void )
{ 
  UBYTE i;

  TRACE_FUNCTION( "ru_tbf_init" );

  grlc_data->ru.rlc_mode   = grlc_data->uplink_tbf.rlc_mode;
  grlc_data->ru.nts        = grlc_data->uplink_tbf.nts; 
  grlc_data->ru.nts_max    = grlc_data->uplink_tbf.nts;
  grlc_data->ru.va         = 0; 
  grlc_data->ul_tn_mask    = grlc_data->uplink_tbf.ts_mask;
#if defined REL99 AND defined TI_PS_FF_TBF_EST_PACCH
  grlc_data->ru.tbf_re_est = FALSE;
#endif

  
  /*
   * initate l1 queue parameters
   */
  grlc_data->ru.write_pos_index   = 0;	
  for(i=0; i<MAX_UL_TN;i++)
    grlc_data->ru.ul_data[i].block_status = 0;


  if(grlc_data->ru.rlc_mode EQ CGRLC_RLC_MODE_ACK)
  {
    /* 
     * parameters only used in acknowledged mode
     */        
    grlc_data->ru.bsn_ret           = 0;     
    grlc_data->ru.last_si_block     = 0;     
    grlc_data->ru.N3104_MAX         = 3 * (grlc_data->uplink_tbf.bs_cv_max + 3 ) * grlc_data->ru.nts_max; 
    grlc_data->ru.nr_nacked_blks    = 0;
    grlc_data->ru.ti                = grlc_data->uplink_tbf.ti;
  }
  else if(grlc_data->ru.rlc_mode EQ CGRLC_RLC_MODE_UACK)
  {
    /* 
     * parameters only used in unacknowledged mode
     */
    grlc_data->ru.count_cv_0        = 0; 
    /* 
     * tlli is not send within data blocks
     */ 
    grlc_data->ru.ti                = 0; 
  }

  /* 
   * read the first pdu 
   */

  grlc_data->ru.active_prim        = grlc_data->prim_start_tbf;
  grlc_data->prim_queue[grlc_data->prim_start_tbf].previous = 0xFF;
  grlc_data->ru.next_prim          = grlc_data->prim_queue[grlc_data->ru.active_prim].next;
  grlc_data->prim_queue[grlc_data->prim_start_tbf].cv_status  = FALSE;
  grlc_data->prim_queue[grlc_data->prim_start_tbf].last_bsn   = 0xFF;
  grlc_data->ru.vs                = 0;
  grlc_data->ru.cv                = 55;
  /*
   * USHORT
   */
  grlc_data->ru.sdu_len           = grlc_data->prim_queue[grlc_data->ru.active_prim].prim_ptr->sdu.l_buf/8;
  grlc_data->ru.sdu_off           = grlc_data->prim_queue[grlc_data->ru.active_prim].prim_ptr->sdu.o_buf/8;
  grlc_data->prim_queue[grlc_data->ru.active_prim].rlc_status = TRUE;
  grlc_data->ru.pdu_sent          = 0;
  grlc_data->ru.cnt_ts            = 0;
  /*
   * ULONG
   */
  grlc_data->missed_poll_fn       = 1;

  /*
   * BOOL
   */
  grlc_data->ru.tlli_cs_type      = grlc_data->uplink_tbf.tlli_cs_type;
  grlc_data->ru.cd_active         = FALSE;
  grlc_data->ru.reorg_l1_needed	  = FALSE;
  grlc_data->ru.v_next_tbf_params = FALSE;
  grlc_data->ul_tfi_changed       = FALSE;
  grlc_data->ru.release_tbf       = FALSE;

  /*
   * Type
   */    
  grlc_data->ru.cs_type           = grlc_data->uplink_tbf.cs_type;
  grlc_data->ru.last_bsn          = LAST_BSN_NOT_BULIT;
  /*
   * struct
   */
  if(grlc_data->uplink_tbf.mac_mode EQ CGRLC_MAC_MODE_DA)
  {
     /*
      * Start t3164, when starting time is elapsed.
      * Only if contention resolution is needed and not fixed alloc is used.
      */
    grlc_data->ru.first_usf   = TRUE;
  }  
  grlc_data->ru.rlc_data_size = ru_calc_rlc_data_size(grlc_data->ru.cs_type, grlc_data->ru.ti);
  grlc_data->ru.block_status  = ru_set_block_status(grlc_data->ru.cs_type);

  /*
   * handle close ended TBF
   */
  if (grlc_data->uplink_tbf.rlc_db_granted )
  {
    ULONG rlc_oct_cnt ;
    
    UBYTE next = grlc_data->prim_queue[grlc_data->prim_start_tbf].next;

    rlc_oct_cnt = (grlc_data->prim_queue[grlc_data->prim_start_tbf].prim_ptr->sdu.l_buf/8)+1;
    
    grlc_data->uplink_tbf.rlc_db_granted *= grlc_data->ru.rlc_data_size;

    while( (next < PRIM_QUEUE_SIZE)                       AND 
           (!grlc_data->prim_queue[next].start_new_tbf)   AND    
           ((rlc_oct_cnt+(grlc_data->prim_queue[next].prim_ptr->sdu.l_buf/8)+1) < grlc_data->uplink_tbf.rlc_db_granted))
    {
      rlc_oct_cnt += (grlc_data->prim_queue[next].prim_ptr->sdu.l_buf/8)+1;
      next         = grlc_data->prim_queue[next].next;
    }
    if(next < PRIM_QUEUE_SIZE)
      grlc_data->prim_queue[next].start_new_tbf = 1;

    TRACE_EVENT_P4("close ended TBF:  rlc_g=%ld rlc_oct =%ld data_size=%d next=%d"
                    ,grlc_data->uplink_tbf.rlc_db_granted
                    ,rlc_oct_cnt
                    ,grlc_data->ru.rlc_data_size
                    ,next);
  }

  grlc_data->ru.rlc_octet_cnt = ru_recalc_rlc_oct_cnt(); /*rlc data size needed*/
  grlc_data->ru.pl_retrans_current.cnt = 0;




  /* TRACE PARAMS*/
  grlc_data->ul_index = 0;        
  grlc_data->tbf_ctrl[grlc_data->ul_index].tbf_type    = TBF_TYPE_UL;
  grlc_data->tbf_ctrl[grlc_data->ul_index].tfi         = grlc_data->ul_tfi;        
  grlc_data->tbf_ctrl[grlc_data->ul_index].rlc_oct_cnt = 0;
  grlc_data->tbf_ctrl[grlc_data->ul_index].ret_bsn     = 0;
  grlc_data->tbf_ctrl[grlc_data->ul_index].ack_cnt     = 0;
  grlc_data->tbf_ctrl[grlc_data->ul_index].fbi         = 0;
} /* ru_tbf_init() */



/*
+------------------------------------------------------------------------------
| Function    : ru_set_T3198
+------------------------------------------------------------------------------
| Description : The function ru_set_T3198() starts the timer T3198 of the rlc
|               data block with bsn as input parameter. The timer is handled in
|               BS_CV_MAX block periods.
|
| Parameters  : bsn_i - bsn value of the data block
|
+------------------------------------------------------------------------------
*/
GLOBAL void ru_set_T3198 ( UBYTE bsn_i )
{ 
  USHORT block_periods;
  
  
  TRACE_FUNCTION( "ru_set_T3198" );


  block_periods = MAXIMUM(1,grlc_data->uplink_tbf.bs_cv_max);
  /*
   * set timer T3198 , 
   * the framenumber is set, at which the block is set to negative acknowledged
   */
  grlc_data->ru.rlc_data[bsn_i & WIN_MOD].T3198 = grlc_decode_tbf_start_rel(grlc_data->ul_fn, --block_periods);

#ifdef _SIMULATION_
    TRACE_EVENT_P4("SET T3198: bsn %d t3198=%ld c_fn=%ld bs_cv_max=%d"
                                                                      ,bsn_i
                                                                      ,grlc_data->ru.rlc_data[bsn_i & WIN_MOD].T3198
                                                                      ,grlc_data->ul_fn
                                                                      ,grlc_data->uplink_tbf.bs_cv_max);
#endif

} /* ru_set_T3198() */

/*
+------------------------------------------------------------------------------
| Function    : ru_send_mac_data_req
+------------------------------------------------------------------------------
| Description : The function ru_send_mac_data_req() sends the data block with
|               bsn_i. The claculated block is matched in the PL buffer  
|               (and MAC_DATA_REQ primitive, if SIMULATION is defined). 
|
| Parameters  : bsn_i - bsn value of the next to sent data block
|
+------------------------------------------------------------------------------
*/
GLOBAL void ru_send_mac_data_req ( UBYTE bsn_i )
{ 
  UBYTE   li_cnt;
  UBYTE   data_cnt;
  UBYTE   i;
  USHORT  l_tlli;
  UBYTE   len;
  USHORT  off;
  UBYTE   index;
  UBYTE   *ptr_temp;
  TRACE_FUNCTION( "ru_send_mac_data_req" );

#ifdef _SIMULATION_
  TRACE_EVENT_P2("BSN=%d mac_header=%x ",bsn_i, grlc_data->ru.rlc_data[bsn_i & WIN_MOD].header.mac);
#endif /* #ifdef _SIMULATION_ */

      

  {
    memset(&grlc_data->ru.ul_data[grlc_data->ru.write_pos_index],
           0, 
           sizeof(T_ul_data));

    grlc_data->ru.ul_data[grlc_data->ru.write_pos_index].block_status = grlc_data->ru.rlc_data[bsn_i & WIN_MOD].block_status;

    ptr_temp  = (UBYTE *) (grlc_data->ru.ul_data[grlc_data->ru.write_pos_index].ul_block);    
    /*
     * set MAC Header 
     */
    ptr_temp[0]  = grlc_data->ru.rlc_data[bsn_i & WIN_MOD].header.mac;  

    ptr_temp[1]  = (grlc_data->ul_tfi <<  1 |
                    grlc_data->ru.rlc_data[bsn_i & WIN_MOD].header.ti);

    ptr_temp[2]  =  (bsn_i  <<  1 |
                    grlc_data->ru.rlc_data[bsn_i & WIN_MOD].header.e_bit);                /*lint !e415*/
    /*
     * set Length Indicator field(s) if present
     */
    li_cnt = grlc_data->ru.rlc_data[bsn_i & WIN_MOD].header.li_cnt;
    for(i=3; i<(3+li_cnt);i++)
      ptr_temp[i] = grlc_data->ru.rlc_data[bsn_i & WIN_MOD].header.li_me[i-3];   /*lint !e661 !e662*/
    /*
     * set TLLI field, if present
     */
    if(grlc_data->ru.rlc_data[bsn_i & WIN_MOD].header.ti)
    {
      grlc_set_tlli( &l_tlli, &off, &(ptr_temp[3+li_cnt]), grlc_data->uplink_tbf.tlli );
      l_tlli /= 8; /* handled in bytes */

#ifdef REL99
      if (grlc_data->pfi_support)
      {
        ptr_temp[1] |= 0x40;
        ptr_temp[3+li_cnt+l_tlli] =(grlc_data->pfi_value << 1) | 0x01;/*04.60 7.1.2.6(initial pfi)*/ /*E bit = 1*/
        l_tlli++;
      } 
#endif

    }
    else
      l_tlli  = 0;
    /* 
     * copy LLC DATA
     */
    index    = 0;
    data_cnt = grlc_data->ru.rlc_data[bsn_i & WIN_MOD].data_cnt;
    for(i=0;i<data_cnt;i++)
    {
      len = grlc_data->ru.rlc_data[bsn_i & WIN_MOD].data[i].l_buf / 8;
      off = grlc_data->ru.rlc_data[bsn_i & WIN_MOD].data[i].o_buf / 8;
      if(!grlc_test_mode_active())
      { /*
         * get regular data
         */
        memcpy(&(ptr_temp[3+li_cnt+l_tlli+index]),
                (grlc_data->ru.rlc_data[bsn_i & WIN_MOD].data[i].ptr_data->buf + off),
                 len);                                                        /*lint !e797*/
      }
      else
      {   
        /* 
         * test mode active, get test data
         */
        if(grlc_data->testmode.mode EQ CGRLC_TEST_RANDOM)
        {  
          /*
           *  CGRLC_TEST_RANDOM
           */
          if (!grlc_data->testmode.no_data_calculation)
          {
            /*
             *  First call in this block
             */
            grlc_prbs(COMPUTE_DATA, len, grlc_data->testmode.ptr_test_data);
            grlc_data->testmode.no_data_calculation = 1;
          }
          
          
          if (grlc_data->ru.nts EQ 1)
          { 
             /*
              * this block is finished, allow prbs data calculation aigain
              */
             grlc_data->testmode.no_data_calculation = 0;
          }  

          /*
           *  copy the prbs in 
           */
          memcpy(&(ptr_temp[3+li_cnt+l_tlli+index]), grlc_data->testmode.ptr_test_data, len); /*lint !e797*/        
        }
        else
        {
          /*
           *  CGRLC_LOOP
           */     
          /* 
           * If the downlink TBF is established on more than one timeslot, the MS shall transmit in 
           * the second uplink timeslot (if present) RLC/MAC blocks received on the second downlink 
           * timeslot, and shall transmit in the third uplink timeslot (if present) RLC/MAC blocks 
           * received in the third downlink timeslot and so on.
           * It is assumed that while transmitting L1 will transmit data blocks in the sequence
           * in which it has received blocks from GRLC.
           */
          if (grlc_data->downlink_tbf.nts > 1)
          {
            if (grlc_data->ru.write_pos_index EQ 0)
            {
              memcpy(&(ptr_temp[3+li_cnt+l_tlli+index]),grlc_data->testmode.rec_data[0].payload,len);
              TRACE_EVENT_P4("bsn =%d ready for send, cs_type=%ld, e_bit=%d, tn=%d"
                                        ,bsn_i
                                        ,grlc_data->ru.ul_data[grlc_data->ru.write_pos_index].block_status
                                        ,grlc_data->testmode.rec_data[0].e_bit
                                        ,grlc_data->ru.write_pos_index);
            }
            else
            {
              memcpy(&(ptr_temp[3+li_cnt+l_tlli+index]),grlc_data->testmode.rec_data[1].payload,len);
              TRACE_EVENT_P4("bsn =%d ready for send, cs_type=%ld, e_bit=%d, tn=%d"
                                        ,bsn_i
                                        ,grlc_data->ru.ul_data[grlc_data->ru.write_pos_index].block_status
                                        ,grlc_data->testmode.rec_data[1].e_bit
                                        ,grlc_data->ru.write_pos_index);
            }
          }
          else
          {
             memcpy(&(ptr_temp[3+li_cnt+l_tlli+index]),grlc_data->testmode.rec_data[0].payload,len);
             TRACE_EVENT_P3("bsn =%d ready for send, cs_type=%ld, e_bit=%d"
                                        ,bsn_i
                                        ,grlc_data->ru.ul_data[grlc_data->ru.write_pos_index].block_status
                                        ,grlc_data->testmode.rec_data[0].e_bit);
          }


        }
      }
      index += len;
    }
    if((grlc_data->ru.rlc_data[bsn_i & WIN_MOD].header.mac & 0x3C) EQ 0) /* countdown value is equal 0*/
    {
      /*
       * fill rest of last block with 2B
       */
      USHORT rlc_data_size;
      rlc_data_size = ru_calc_rlc_data_size( ru_get_cs_type(grlc_data->ru.rlc_data[bsn_i & WIN_MOD].block_status)
                      , grlc_data->ru.rlc_data[bsn_i & WIN_MOD].header.ti);
      len = rlc_data_size-index-li_cnt;
      memset(&(ptr_temp[3+li_cnt+l_tlli+index]),
              0x2B,
               len);                              /*lint !e797*/
    }

#ifdef _SIMULATION_
  {
    PALLOC(mac_data_req,MAC_DATA_REQ);
    memset(&(mac_data_req->ul_data),
           0, 
           sizeof(T_ul_data));
    memcpy(&(mac_data_req->ul_data),
           &(grlc_data->ru.ul_data[grlc_data->ru.write_pos_index]),
           sizeof(T_ul_data));                                    /*lint !e797*/
    PSEND(hCommL1,mac_data_req);
  }
  TRACE_EVENT_P1("wpi %d",grlc_data->ru.write_pos_index);
#else  /* #ifdef _SIMULATION_ */
  {
    TRACE_MEMORY_PRIM ( hCommGRLC, hCommL1, MAC_DATA_REQ,
                        &grlc_data->ru.ul_data[grlc_data->ru.write_pos_index],
                        sizeof(T_ul_data) );
  }
#endif /* #ifdef _SIMULATION_ */
      
    grlc_data->ru.write_pos_index++;
    
    /*
     * indicates the PL, there is no more block
     */
    grlc_data->ru.ul_data[grlc_data->ru.write_pos_index].block_status = 0;
  }
  grlc_data->ru.pl_retrans_current.blk[grlc_data->ru.pl_retrans_current.cnt] = bsn_i;
  grlc_data->ru.pl_retrans_current.cnt++;
  grlc_data->ru.nts--;
  grlc_data->ru.vb[bsn_i & WIN_MOD] = VB_PENDING_ACK;
} /* ru_send_mac_data_req() */


  

/*
+------------------------------------------------------------------------------
| Function    : ru_set_prim_queue
+------------------------------------------------------------------------------
| Description : The function ru_set_prim_queue() updates the primitive queue.
|               If the countdown procedure is started, all LLC PDUs which belongs
|               to the current tbf are signed with cv_status as true. In this case
|               these LLC PDUs can´t reorginized an more LLC PDU can sent during 
|               his TBF. Additionally received LLC PDUs are sent in a new TBF.
|               If the countdown procedure is stopped (i.e. change of cpding 
|               scheme) then this functions resets the cv_status to False.
|
| Parameters  : cd_state_i - if true, the countdown procedue is started, else
|               it is stopped
|
+------------------------------------------------------------------------------
*/
GLOBAL void ru_set_prim_queue ( BOOL cd_state_i )
{ 
  UBYTE next;
  TRACE_FUNCTION( "ru_set_prim_queue" );

  next = grlc_data->ru.next_prim;
  grlc_data->prim_queue[grlc_data->ru.active_prim].cv_status = cd_state_i;

  while( (grlc_data->prim_queue[next].start_new_tbf EQ 0) AND (next NEQ 0xFF) )
  {
    grlc_data->prim_queue[next].cv_status = cd_state_i;
    next = grlc_data->prim_queue[next].next;
  }

  
} /* ru_set_prim_queue() */




/*
+------------------------------------------------------------------------------
| Function    : ru_countdown_procedure
+------------------------------------------------------------------------------
| Description : The function ru_countdown_procedure() calculates the countdown
|               value. After Contention resoultion is succesfully completed or
|               at changing of the Coding scheme(indicateded in packet 
|               uplink ack/nack or packet timeslot reconfigure), the countdown 
|               value must be recalculated. 
|
| Parameters  : dummy - description of parameter dummy
|
+------------------------------------------------------------------------------
*/
GLOBAL UBYTE ru_countdown_procedure ( UBYTE ret_blocks )
{ 
  USHORT  x;
  UBYTE   result;
  TRACE_FUNCTION( "ru_countdown_procedure" );

  /*
   * ret_blocks is the number of blocks which has to retransmit
   * formular for cv calculation: x = round((TBC-bsn´-1)/nts)
   * here tbc = (TBC-bsn) so that
   *       x  = round((tbc-1)/nts)
   * nr of pdu bundaries is also considered for calculating tbc
   */
  
#ifdef _SIMULATION_
  TRACE_EVENT_P3("bs_cv_max=%d   rlc_oct_cnt=%d data_size=%d",grlc_data->uplink_tbf.bs_cv_max,grlc_data->ru.rlc_octet_cnt,grlc_data->ru.rlc_data_size);
#endif /* #ifdef _SIMULATION_ */


  if(grlc_data->testmode.mode EQ CGRLC_LOOP)
  {
    TRACE_EVENT("testmode B: cv value=15");
    return (15); 
  }

  grlc_data->ru.tbc = (grlc_data->ru.rlc_octet_cnt+grlc_data->ru.pdu_boundaries) / grlc_data->ru.rlc_data_size;
  if( (grlc_data->ru.rlc_octet_cnt+grlc_data->ru.pdu_boundaries) % grlc_data->ru.rlc_data_size) /*round upwards*/
    grlc_data->ru.tbc++;


  x   = (grlc_data->ru.tbc -1 + ret_blocks) / grlc_data->ru.nts_max; 

  if( (grlc_data->ru.tbc -1 + ret_blocks ) % grlc_data->ru.nts_max) /*round upwards*/
    x++;


  if(x > grlc_data->uplink_tbf.bs_cv_max)
  {
    result = 15;
    if(grlc_data->ru.cd_active)
    {
      grlc_data->ru.cd_active = FALSE;
      ru_set_prim_queue(grlc_data->ru.cd_active);
    }
  }
  else
  { 
    result = (UBYTE)x;
    if(!grlc_data->ru.cd_active AND 
      ((grlc_data->ru.state NEQ RU_WAIT_FOR_FIRST_CALL_ACK) OR (grlc_data->ru.state NEQ RU_WAIT_FOR_FIRST_CALL_UACK)))
    {
      grlc_data->ru.cd_active = TRUE;
      ru_set_prim_queue(grlc_data->ru.cd_active);
    }
  }

  return result;
  
} /* ru_countdown_procedure() */




/*
+------------------------------------------------------------------------------
| Function    : ru_update_vb
+------------------------------------------------------------------------------
| Description : The function ru_update_vb() refreshs the VB array field after
|               receiving a packet uplink ack/nack.
|
| Parameters  : dummy - description of parameter dummy
|
+------------------------------------------------------------------------------
*/
GLOBAL void ru_update_vb ( void )
{ 
  MCAST(d_ul_ack,D_GRLC_UL_ACK);
  UBYTE bsn, ssn, i;
  TRACE_FUNCTION( "ru_update_vb" );

  ssn = d_ul_ack->gprs_ul_ack_nack_info.ack_nack_des.ssn;   
  /* 
   * Check if ssn is valid
   */
  if(!ru_within_window(ssn,grlc_data->ru.vs,grlc_data->ru.va))
  {
    TRACE_EVENT_P4( "SSN OUTSIDE WINDOW: ssn=%d, vs=%d, va=%d f_ack_ind=%d",
                                ssn,
                                grlc_data->ru.vs,
                                grlc_data->ru.va, 
                                d_ul_ack->gprs_ul_ack_nack_info.ack_nack_des.f_ack_ind);
      return;
  }
  grlc_data->ru.nr_nacked_blks = 0;
  for(i = 1; i <= WIN_SIZE; i++)
  {
    bsn = (ssn - i) & 0x7F;  /*mod 128*/
    if(d_ul_ack->gprs_ul_ack_nack_info.ack_nack_des.rbb[WIN_SIZE-i])
    {
      if(grlc_data->ru.vb[bsn & WIN_MOD] NEQ VB_ACKED)
      {
        grlc_data->ru.vb[bsn & WIN_MOD] = VB_ACKED;
      }
    }
    else if(grlc_check_if_tbf_start_is_elapsed ( grlc_data->ru.rlc_data[bsn & WIN_MOD].T3198, grlc_data->ul_fn))
    {
      /*
       * timeout T3198
       */
      grlc_data->ru.vb[bsn & WIN_MOD] = VB_NACKED;
      grlc_data->ru.nr_nacked_blks++;
    }
    else
    {
      TRACE_EVENT_P3("BSN =%d not acked. T3198=%ld not expired (c_fn=%ld). vb not modified"
                                                                        ,bsn
                                                                        ,grlc_data->ru.rlc_data[bsn & WIN_MOD].T3198
                                                                        ,grlc_data->ul_fn);
    }
    if (bsn EQ grlc_data->ru.va)
      i = WIN_SIZE+1; /* break cobdition*/
  }

} /* ru_update_vb() */




/*
+------------------------------------------------------------------------------
| Function    : ru_calc_rlc_data_block
+------------------------------------------------------------------------------
| Description : The funcion ru_calc_rlc_data_block() builds a complete RLC
|               data block. 
|
| Parameters  : bsn_i - bsn value of the calculated RLC data block
|
+------------------------------------------------------------------------------
*/
GLOBAL void ru_calc_rlc_data_block ( UBYTE bsn_i )
{ 
  USHORT  rlc_len;
  UBYTE   li_cnt, data_cnt;
  TRACE_FUNCTION( "ru_calc_rlc_data_block" );
  
  /* 
   * calculate the countdown value, no retransmission
   */
  grlc_data->ru.cv = ru_countdown_procedure(0);
  grlc_data->ru.rlc_data[bsn_i & WIN_MOD].block_status = grlc_data->ru.block_status;  

  /*
   * set MAC header
   */
  grlc_data->ru.rlc_data[bsn_i & WIN_MOD].header.mac = (grlc_data->ru.cv  <<  2 |
                                             grlc_data->r_bit            );
  /*
   * set RLC header values
   */
  grlc_data->ru.rlc_data[bsn_i & WIN_MOD].header.ti      = grlc_data->ru.ti;

  /* 
   * data handling 
   */
  rlc_len = grlc_data->ru.rlc_data_size;
  data_cnt = 0;
  li_cnt   = 0;

  if(grlc_data->testmode.mode EQ CGRLC_LOOP)
  {
    grlc_data->ru.sdu_len       = 7777;                       /* random value */
    grlc_data->ru.sdu_off       = 0;
    rlc_len                    = 50;
    grlc_data->ru.rlc_octet_cnt = 7777;                       /* to be in line with sdu_len*/
    TRACE_EVENT("testmode B: get data from received downlink"); 
  }


    
  while ( (grlc_data->ru.sdu_len < rlc_len)  AND  (grlc_data->ru.sdu_len > 0) )
  { 
    /* 
     * end of pdu in block reached
     */
    grlc_data->ru.rlc_data[bsn_i & WIN_MOD].header.li_me[li_cnt]= (UBYTE) grlc_data->ru.sdu_len;
    grlc_data->ru.rlc_data[bsn_i & WIN_MOD].header.li_me[li_cnt] <<= 2;

    grlc_data->ru.rlc_data[bsn_i & WIN_MOD].data[li_cnt].ptr_data  = 
                    &(grlc_data->prim_queue[grlc_data->ru.active_prim].prim_ptr->sdu);
    grlc_data->ru.rlc_data[bsn_i & WIN_MOD].data[li_cnt].l_buf     = grlc_data->ru.sdu_len * 8;
    grlc_data->ru.rlc_data[bsn_i & WIN_MOD].data[li_cnt].o_buf     = grlc_data->ru.sdu_off * 8;
    rlc_len -= grlc_data->ru.sdu_len + 1;
    grlc_data->ru.rlc_octet_cnt -= grlc_data->ru.sdu_len;
    grlc_data->ru.sdu_len       -= grlc_data->ru.rlc_data[bsn_i & WIN_MOD].data[li_cnt].l_buf/8;
    grlc_data->ru.sdu_off       += grlc_data->ru.rlc_data[bsn_i & WIN_MOD].data[li_cnt].l_buf/8;
    ru_get_next_sdu();
    if((grlc_data->ru.sdu_len > 0) AND rlc_len)
    {
      /*
       * one more sdu in queue
       */
      grlc_data->ru.rlc_data[bsn_i & WIN_MOD].header.li_me[li_cnt]   |=  0x02;
    }
    else
    {
      /* 
       * no more sdu in queue 
       */
      grlc_data->ru.rlc_data[bsn_i & WIN_MOD].header.li_me[li_cnt]   |=  0x01;
    }
    data_cnt++;
    li_cnt++;
    if(li_cnt > NR_OF_PDUS_PER_RLCMAC_BLOCK)
    {
      TRACE_EVENT_P2("li_cnt=%d NR_OF_PDUS_PER_RLCMAC_BLOCK=%d",li_cnt,NR_OF_PDUS_PER_RLCMAC_BLOCK);
      TRACE_ERROR("ru li_cnt bigger than RD_LI_CNT_MAX (=8)");
      TRACE_ASSERT( li_cnt > NR_OF_PDUS_PER_RLCMAC_BLOCK );
      return;
    }
  }
  
  if((grlc_data->ru.sdu_len EQ rlc_len) AND  (grlc_data->ru.sdu_len > 0) AND (grlc_data->ru.cv EQ 0))
  { 
    /*
     * end of pdu; rest of pdu match exactly into the rest of a rlc data block, last uplink block
     */
    grlc_data->ru.rlc_data[bsn_i & WIN_MOD].data[li_cnt].ptr_data      = 
                       &(grlc_data->prim_queue[grlc_data->ru.active_prim].prim_ptr->sdu);       /*lint !e661*/

    grlc_data->ru.rlc_data[bsn_i & WIN_MOD].data[li_cnt].l_buf         = grlc_data->ru.sdu_len *8;        /*lint !e661*/
    grlc_data->ru.rlc_data[bsn_i & WIN_MOD].data[li_cnt].o_buf         = grlc_data->ru.sdu_off *8;        /*lint !e661*/
    data_cnt++;
    grlc_data->ru.rlc_octet_cnt -= rlc_len;
    ru_get_next_sdu();
  }
  else if((grlc_data->ru.sdu_len EQ rlc_len) AND  (grlc_data->ru.sdu_len > 0))
  { 
    /*
     * end of pdu; rest of pdu match exactly into the rest of a rlc data block,
     * split into two blocks, len set to zero, indicates that the end of pdu is not reached
     */
    grlc_data->ru.rlc_data[bsn_i & WIN_MOD].header.li_me[li_cnt]  =  0x01; /*lint !e661*/

    grlc_data->ru.rlc_data[bsn_i & WIN_MOD].data[li_cnt].ptr_data      = 
                       &(grlc_data->prim_queue[grlc_data->ru.active_prim].prim_ptr->sdu);       /*lint !e661*/
    grlc_data->ru.rlc_data[bsn_i & WIN_MOD].data[li_cnt].l_buf         = (grlc_data->ru.sdu_len-1) *8;    /*lint !e661*/
    grlc_data->ru.rlc_data[bsn_i & WIN_MOD].data[li_cnt].o_buf         = grlc_data->ru.sdu_off *8;        /*lint !e661*/
    data_cnt++;
    li_cnt++;
    rlc_len--;
    grlc_data->ru.sdu_len       -= rlc_len;
    grlc_data->ru.sdu_off       += rlc_len;
    grlc_data->ru.rlc_octet_cnt -= rlc_len;         
    if(li_cnt > NR_OF_PDUS_PER_RLCMAC_BLOCK)
    {
      TRACE_EVENT_P2("li_cnt=%d NR_OF_PDUS_PER_RLCMAC_BLOCK=%d",li_cnt,NR_OF_PDUS_PER_RLCMAC_BLOCK);
      TRACE_ERROR("ru2 li_cnt bigger than RD_LI_CNT_MAX (=8)");
      TRACE_ASSERT( li_cnt > NR_OF_PDUS_PER_RLCMAC_BLOCK );
      return;
    }
  }
  else   if( (grlc_data->ru.sdu_len > 0)  AND rlc_len)
  { 
    /* 
     * only a part of the sdu matches into the rlc  data block
     */
    if(grlc_data->testmode.mode EQ CGRLC_LOOP)
    {
      grlc_data->ru.rlc_data[bsn_i & WIN_MOD].data[li_cnt].ptr_data = NULL;                               /*lint !e661*/
      TRACE_EVENT("set data ptr for testmode B to NULL");
    }
    else
    {
      grlc_data->ru.rlc_data[bsn_i & WIN_MOD].data[li_cnt].ptr_data = 
                   &(grlc_data->prim_queue[grlc_data->ru.active_prim].prim_ptr->sdu);           /*lint !e661*/
    }
        grlc_data->ru.rlc_data[bsn_i & WIN_MOD].data[li_cnt].l_buf    = rlc_len * 8;                      /*lint !e661*/
    grlc_data->ru.rlc_data[bsn_i & WIN_MOD].data[li_cnt].o_buf    = grlc_data->ru.sdu_off * 8;            /*lint !e661*/
    data_cnt++;
    grlc_data->ru.sdu_len       -= rlc_len;
    grlc_data->ru.sdu_off       += rlc_len;
    grlc_data->ru.rlc_octet_cnt -= rlc_len;
  }
  /*
   * LI fied parameters are set
   */
  if(grlc_data->testmode.mode EQ CGRLC_LOOP)
  {
    grlc_data->ru.rlc_data[bsn_i & WIN_MOD].header.e_bit = grlc_data->testmode.rec_data[0].e_bit;
  }
  else if(li_cnt)
  {
    grlc_data->ru.rlc_data[bsn_i & WIN_MOD].header.e_bit            = 0;
    grlc_data->ru.rlc_data[bsn_i & WIN_MOD].header.li_me[li_cnt-1] |= 0x01;
        grlc_data->ru.pdu_boundaries -= li_cnt;
#ifdef _SIMULATION_
  TRACE_EVENT_P2("li_cnt=%d,remaining pdu boundaries=%d",
                                                        li_cnt,
                                                        grlc_data->ru.pdu_boundaries);
#endif /* #ifdef _SIMULATION_ */

  }
  else
  {
    grlc_data->ru.rlc_data[bsn_i & WIN_MOD].header.e_bit = 1;
  }

  grlc_data->ru.rlc_data[bsn_i & WIN_MOD].header.li_cnt = li_cnt;
  grlc_data->ru.rlc_data[bsn_i & WIN_MOD].data_cnt      = data_cnt;
  


} /* ru_calc_rlc_data_block() */


/*
+------------------------------------------------------------------------------
| Function    : ru_handle_n3102
+------------------------------------------------------------------------------
| Description : The function ru_handle_n3102() handles the counter N3102.
|               If the input parameter ia PAN_INC, then the counter is 
|               incremented. If the input parameter ia PAN_DEC, then the 
|               counter is decremented. 
|
| Parameters  : pan_cnt_i - determines, if the counter is either in- or 
|               decremented
|
+------------------------------------------------------------------------------
*/
GLOBAL void ru_handle_n3102 ( T_PAN_CNT  pan_cnt_i )
{ 
  TRACE_FUNCTION( "ru_handle_n3102" );

  if((grlc_data->N3102 NEQ 0xFF)   AND 
     grlc_data->pan_struct.inc     AND
     grlc_data->pan_struct.dec     AND
     grlc_data->pan_struct.pmax)
  {

    switch( pan_cnt_i )
    {
    case PAN_INC:
      grlc_data->N3102 += grlc_data->pan_struct.inc;  
      grlc_data->N3102 = MINIMUM(grlc_data->N3102,grlc_data->pan_struct.pmax);
      
      /*TRACE_EVENT_P4( "INC N3102: inc=%d, dec=%d, pan_max=%d n3102=%d",
                      grlc_data->pan_struct.inc,
                      grlc_data->pan_struct.dec,
                      pgrlc_data->pan_struct.pmax,
                      grlc_data->N3102 );*/
      break;
    case PAN_DEC:
      if( grlc_data->N3102 > grlc_data->pan_struct.dec )
      {
        grlc_data->N3102 -= grlc_data->pan_struct.dec;

        TRACE_EVENT_P4( "DEC1 N3102: inc=%d, dec=%d, pan_max=%d n3102=%d",
                        grlc_data->pan_struct.inc,
                        grlc_data->pan_struct.dec,
                        grlc_data->pan_struct.pmax,
                        grlc_data->N3102 );

        sig_ru_tm_error_ra();
      }
      else
      {
        grlc_data->N3102 = 0;

        TRACE_EVENT_P4( "DEC2 N3102: inc=%d, dec=%d, pan_max=%d n3102=%d",
                        grlc_data->pan_struct.inc,
                        grlc_data->pan_struct.dec,
                        grlc_data->pan_struct.pmax,
                        grlc_data->N3102);
        sig_ru_tm_error_ra();
      }
      break;
    default:
      TRACE_ERROR("unknown type for pan_cnt_i");
      break;
    }
  }
  else if(pan_cnt_i EQ PAN_DEC)
  {
    TRACE_EVENT( "IGNORE N3102" );

    sig_ru_tm_error_ra();
  }
} /* ru_handle_n3102() */



/*
+------------------------------------------------------------------------------
| Function    : ru_calc_va
+------------------------------------------------------------------------------
| Description : The function ru_calc_va() claculates the block, that was
|               negatively acknowledged and must be retransmitted in the next
|               uplink block.
|
| Parameters  : dummy - description of parameter dummy
|
+------------------------------------------------------------------------------
*/
GLOBAL UBYTE ru_calc_va ( void )
{ 
  UBYTE result;
  TRACE_FUNCTION( "ru_calc_va" );

  result = (grlc_data->ru.va) & 0x7F /*mod 128*/;
  
  while( (grlc_data->ru.vb[result & WIN_MOD] EQ VB_ACKED) AND
         (result NEQ grlc_data->ru.vs))
  {
    result = (result+1) & 0x7F /*mod 128*/;
  }  
  return result;
} /* ru_calc_va() */


/*
+------------------------------------------------------------------------------
| Function    : ru_set_next_bsn_ret()
+------------------------------------------------------------------------------
| Description : The function ru_set_next_bsn_ret sets the next valid bsn, 
|               which shall be transmitted as next retransmission 
|
| Parameters  :  - description of parameter dummy
|
+------------------------------------------------------------------------------
*/

GLOBAL UBYTE ru_set_next_bsn_ret ( void)
{ 
  UBYTE new_bsn_ret;
  TRACE_FUNCTION( "ru_set_next_bsn_ret" );

     
  new_bsn_ret = (grlc_data->ru.bsn_ret+1) & 0x7F; 
  while( (grlc_data->ru.vb[new_bsn_ret & WIN_MOD] NEQ VB_PENDING_ACK) AND
         (grlc_data->ru.vb[new_bsn_ret & WIN_MOD] NEQ VB_NACKED)      AND 
          new_bsn_ret NEQ grlc_data->ru.vs )
  {  
    new_bsn_ret = (new_bsn_ret+1) & 0x7F;    
  }
  if( new_bsn_ret EQ grlc_data->ru.vs )
  {
    grlc_data->ru.nr_nacked_blks = 0 ;
    new_bsn_ret = grlc_data->ru.va;      
  }

  return new_bsn_ret;

} /* ru_set_next_bsn_ret() */


/*
+------------------------------------------------------------------------------
| Function    : ru_ret_bsn
+------------------------------------------------------------------------------
| Description : The function ru_ret_bsn() is called in RU_REL_ACK. In this state
|               all RLC data blocks are transmitted, but not acknowledged. If
|               more uplink PDCHs are available(i.e. the PL reads valid USF 
|               flags), then the blocks which currently where not acked are 
|               retransmitted. The bsn value of the next block is controled by
|               bsn_ret. 
|
| Parameters  : dummy 
|
+------------------------------------------------------------------------------
*/
GLOBAL void ru_ret_bsn ( void )
{ 
  
  TRACE_FUNCTION( "ru_ret_bsn" );

 /* TRACE_EVENT_P3("BSN RET: bsn=%d, cv=%d vb=%d"
                                        ,grlc_data->ru.bsn_ret
                                        ,grlc_data->ru.rlc_data[grlc_data->ru.bsn_ret & WIN_MOD].header.cv
                                        ,grlc_data->ru.vb[grlc_data->ru.bsn_ret & WIN_MOD]);
  */
  ru_send_mac_data_req(grlc_data->ru.bsn_ret);
  grlc_data->ru.bsn_ret = ru_set_next_bsn_ret();

} /* ru_ret_bsn() */





/*
+------------------------------------------------------------------------------
| Function    : ru_change_of_cs
+------------------------------------------------------------------------------
| Description : The function ru_change_of_cs() handles the change of the coding
|               scheme. Folling values must be recalculated.
|                 1. rlc_data_len
|                 2. rlc_octet_cnt
|
| Parameters  : cs_type_i - new coding scheme
|
+------------------------------------------------------------------------------
*/
GLOBAL void ru_change_of_cs ( T_CODING_SCHEME  cs_type_i )
{ 
  TRACE_FUNCTION( "ru_change_of_cs" );

  grlc_data->ru.rlc_data_size = ru_calc_rlc_data_size(cs_type_i,grlc_data->ru.ti);
  grlc_data->ru.block_status  = ru_set_block_status(grlc_data->ru.cs_type);
  if(grlc_data->ru.last_bsn NEQ LAST_BSN_IS_SENT)
  {
    grlc_data->ru.rlc_octet_cnt   = ru_recalc_rlc_oct_cnt();
    grlc_data->ru.reorg_l1_needed = TRUE;
  }
  else
  {
    TRACE_EVENT_P3("No REORG dueto CS change requried: rlc_cnt=%ld l_bsn=%d cv=%d"
                                                                    ,grlc_data->ru.rlc_octet_cnt
                                                                    ,grlc_data->ru.last_bsn
                                                                    ,grlc_data->ru.cv);
  }
} /* ru_change_of_cs() */



/*
+------------------------------------------------------------------------------
| Function    : ru_contention_resolution
+------------------------------------------------------------------------------
| Description : The function ru_contention_resolution() checks at one phase 
|               access after receiving a packet uplink ack/nack message the 
|               contention resolution TLLI.
|
| Parameters  : dummy - description of parameter dummy
|
+------------------------------------------------------------------------------
*/
GLOBAL BOOL ru_contention_resolution ( void )
{ 
  MCAST(d_ul_ack,D_GRLC_UL_ACK);
  BOOL result = FALSE;

  TRACE_FUNCTION( "ru_contention_resolution" );

  if((grlc_data->ru.ti) AND (d_ul_ack->gprs_ul_ack_nack_info.v_cr_tlli) )
  {
    ULONG tlli;
    /* 
     * TLLI received and Contention resolution not yet processed 
     */
    tlli = grlc_buffer2ulong( &d_ul_ack->gprs_ul_ack_nack_info.cr_tlli );
    if(tlli  EQ grlc_data->uplink_tbf.tlli)
      result = TRUE;
    grlc_data->ru.N3104 = 0;
    vsi_t_stop(GRLC_handle,T3166);  
    if(result)
    { 
      /* 
       * correct TLLI received, contention resolution succesful 
       */
      grlc_data->ru.ti = 0;
      grlc_data->ru.rlc_data_size = ru_calc_rlc_data_size(grlc_data->ru.cs_type,grlc_data->ru.ti);
      grlc_data->ru.block_status  = ru_set_block_status(grlc_data->ru.cs_type);
      /* 
       * needed, because nr of pdu boundaries could be change
       */
      grlc_data->ru.rlc_octet_cnt = ru_recalc_rlc_oct_cnt(); 
      sig_ru_tm_cs();
      result = FALSE;
	  grlc_data->ru.reorg_l1_needed = TRUE;  
    }
    else
    {
      /* 
       * wrong TLLI received, contention resolution failed, TBF will be aborted
       */
      result = TRUE;
      TRACE_ERROR( "TLLI error occured" );
      sig_ru_tm_error_ra();
    }
  }
  else if(grlc_data->ru.ti EQ 0)
    result = FALSE;

  return result;
  
} /* ru_contention_resolution() */



/*
+------------------------------------------------------------------------------
| Function    : ru_delete_prims
+------------------------------------------------------------------------------
| Description : The function ru_delete_prims() deletes LLC PDUs from the 
|               primitive queue which are positivlely acknowledged(indirectly 
|               by the highest bsn value in the rrb field included in the 
|               packet uplink ack/nack).
|
| Parameters  : last_bsn_i - all primitives including last_bsn_i are deleted.
|
+------------------------------------------------------------------------------
*/
GLOBAL void ru_delete_prims ( UBYTE last_bsn_i )
{ 
  UBYTE cnt=0, prim_type=CGRLC_PRIM_TYPE_OTHER;
  BOOL  all_null_frames = TRUE;

  TRACE_FUNCTION( "ru_delete_prims" );
  /* 
   * check if last_bsn_i is outside window 
   */
  while( (grlc_data->prim_start_tbf < PRIM_QUEUE_SIZE_TOTAL) AND
         (grlc_data->prim_queue[grlc_data->prim_start_tbf].last_bsn NEQ 0xFF) AND 
         (ru_within_window( last_bsn_i,
                            grlc_data->ru.vs,
                            grlc_data->prim_queue[grlc_data->prim_start_tbf].last_bsn)))
  {
    cnt++;

    if( (prim_type NEQ CGRLC_PRIM_TYPE_GMM) AND 
        (grlc_data->prim_queue[grlc_data->prim_start_tbf].prim_ptr->cause EQ GRLC_DTACS_MOBILITY_MANAGEMENT) )
    {
      prim_type = CGRLC_PRIM_TYPE_GMM;
    }

    if( (all_null_frames EQ TRUE) AND
        (grlc_data->prim_queue[grlc_data->prim_start_tbf].prim_ptr->cause NEQ 
         GRLC_DTACS_CELL_NOTIFI_NULL_FRAME) )
    {
      /* This variable is set to FALSE in case there is at least ont not NULL frame */
      all_null_frames = FALSE;
    }

    sig_ru_tm_prim_delete();
  }

  if(cnt)
  {
    if( grlc_data->ready_timer.handling  EQ  READY_TIMER_HANDLING_ENABLED AND
        all_null_frames                  EQ  FALSE                        AND
        grlc_data->ready_timer.value     NEQ CGRLC_STANDBY                    )
    {
      if (grlc_data->ready_timer.value NEQ CGRLC_DEACTIVATED)
      {
        /* The Ready Timer will be restarted in case of at least one not NULL frame and 
           valid value (<>0 and <> 0xFFFFFFFF) */
        vsi_t_start(GRLC_handle,T3314, grlc_data->ready_timer.value );
      }

      grlc_enter_ready_state( );
    }

    if(   grlc_data->ready_timer.handling EQ READY_TIMER_HANDLING_DISABLED       OR
        ( grlc_data->ready_timer.handling EQ READY_TIMER_HANDLING_ENABLED  AND
          prim_type                       EQ CGRLC_PRIM_TYPE_GMM               )    )
    {
      PALLOC(cgrlc_trigger_ind,CGRLC_TRIGGER_IND); /* T_CGRLC_TRIGGER_IND */
      cgrlc_trigger_ind->prim_type = prim_type;
      PSEND(hCommGMM,cgrlc_trigger_ind);
    }
  }
 

 
#ifdef _SIMULATION_
  TRACE_EVENT_P5("%d PRIMS deleted: last_bsn=%d  vs=%d prim_start_tbf=%d, prim_cnt=%d ",
                   cnt,last_bsn_i,grlc_data->ru.vs,grlc_data->prim_start_tbf,grlc_data->grlc_data_req_cnt); 
  
  TRACE_EVENT_P3("pdu_cnt=%d,pdu_sent=%d,pdu_rem=%d",
                                                    grlc_data->ru.pdu_cnt,
                                                    grlc_data->ru.pdu_sent,
                                                    grlc_data->ru.pdu_rem );
#endif /* #ifdef _SIMULATION_ */  
} /* ru_delete_prims() */



/*
+------------------------------------------------------------------------------
| Function    : ru_handle_n3104
+------------------------------------------------------------------------------
| Description : The function ru_handle_n3104() controls the counter N3104. If
|               the counter reaches it maximum value, then the tbf is released.
|               The counter is ignored, if the contention resolution is 
|               successfully completed. Evry time at receiption a packet uplink
|               ack/nack without including TLLI the counter is incremented. 
|
| Parameters  : dummy - description of parameter dummy
|
+------------------------------------------------------------------------------
*/
GLOBAL BOOL ru_handle_n3104 ( void)
{ 
  UBYTE result = FALSE;
  TRACE_FUNCTION( "ru_handle_n3104" );

  if(grlc_data->ru.ti)
  {
    grlc_data->ru.N3104 = (UBYTE)grlc_data->ru.cnt_ts;
    if(grlc_data->ru.N3104 EQ grlc_data->ru.N3104_MAX)
    {      
      result = TRUE;
      vsi_t_stop(GRLC_handle,T3166);
    }
  }
  return result;
  
} /* ru_handle_n3104() */



/*
+------------------------------------------------------------------------------
| Function    : ru_send_control_block
+------------------------------------------------------------------------------
| Description : The function ru_send_control_block() sends a control block
|               instead of RLC data block
|
| Parameters  : dummy - description of parameter dummy
|
+------------------------------------------------------------------------------
*/
GLOBAL void ru_send_control_block ( void )
{
  UBYTE index;

  TRACE_FUNCTION( "ru_send_control_block" );

#ifdef _SIMULATION_

  memset( &grlc_data->ru.ul_data[grlc_data->ru.write_pos_index],
          0, sizeof( T_ul_data ) );

#endif /* #ifdef _SIMULATION_ */

  grlc_data->ru.ul_data[grlc_data->ru.write_pos_index].block_status = 2;

  index = tm_cpy_ctrl_blk_to_buffer
            ( ( UBYTE* )grlc_data->ru.ul_data[grlc_data->ru.write_pos_index].ul_block );

#ifdef _SIMULATION_
  {
    PALLOC(mac_data_req,MAC_DATA_REQ);
    memset(&(mac_data_req->ul_data),
           0, 
           sizeof(T_ul_data));
    memcpy(&(mac_data_req->ul_data),
           &(grlc_data->ru.ul_data[grlc_data->ru.write_pos_index]),
           sizeof(T_ul_data));
    PSEND(hCommL1,mac_data_req);
  }
  TRACE_EVENT_P1("wpi %d",grlc_data->ru.write_pos_index);
#else  /* #ifdef _SIMULATION_ */
  {
    TRACE_MEMORY_PRIM ( hCommGRLC, hCommL1, MAC_DATA_REQ,
                        &grlc_data->ru.ul_data[grlc_data->ru.write_pos_index],
                        sizeof(T_ul_data) );
  }
#endif /* #ifdef _SIMULATION_ */

  {
    UBYTE* ul_block = ( UBYTE* )grlc_data->ru.ul_data[grlc_data->ru.write_pos_index].ul_block;

    TRACE_BINDUMP( hCommGRLC, TC_USER4,
                   cl_rlcmac_get_msg_name
                     ( ( UBYTE )( ul_block[1] >> 2 ), RLC_MAC_ROUTE_UL ),
                   ul_block, MAX_L2_FRAME_SIZE ); /*lint !e569*/
  }

  grlc_data->ru.write_pos_index++;
  grlc_data->ru.ul_data[grlc_data->ru.write_pos_index].block_status = 0;


  grlc_data->ru.pl_retrans_current.blk[grlc_data->ru.pl_retrans_current.cnt] = 
                                                 index + OFFSET_CTRL_BLOCK_IDX;
  grlc_data->ru.pl_retrans_current.cnt++;
  grlc_data->ru.nts--;  


} /* ru_send_control_block() */


/*
+------------------------------------------------------------------------------
| Function    : ru_stall_ind
+------------------------------------------------------------------------------
| Description : The function ru_stall_ind() handles the stall indication. 
|               If stall indication occurs, the the window is retransmitted if
|               uplink PDCHs are available.
|
| Parameters  : dummy - description of parameter dummy
|
+------------------------------------------------------------------------------
*/
GLOBAL void ru_stall_ind ( void)
{ 
  TRACE_FUNCTION( "ru_stall_ind" );

  /* set the si bit */
  grlc_data->ru.rlc_data[grlc_data->ru.last_si_block & WIN_MOD].header.mac |= 0x02;
  /* send block with set si bit */
  ru_send_mac_data_req(grlc_data->ru.last_si_block);  
  /* reset the si bit */
  grlc_data->ru.rlc_data[grlc_data->ru.last_si_block & WIN_MOD].header.mac &= 0xFD;

  grlc_data->ru.last_si_block = (grlc_data->ru.last_si_block+1)  & 0x7F;  
  if(grlc_data->ru.last_si_block EQ grlc_data->ru.vs)
    grlc_data->ru.last_si_block = grlc_data->ru.va ;

} /* ru_stall_ind() */


/*
+------------------------------------------------------------------------------
| Function    : ru_new_data
+------------------------------------------------------------------------------
| Description : The function ru_new_data() calculates the next in sequence data
|               and transmits it.
|
| Parameters  : dummy - description of parameter dummy
|
+------------------------------------------------------------------------------
*/
GLOBAL void ru_new_data ( void)
{ 
  TRACE_FUNCTION( "ru_new_data" );

  ru_calc_rlc_data_block(grlc_data->ru.vs);
  ru_send_mac_data_req(grlc_data->ru.vs);
  grlc_data->ru.rlc_data[grlc_data->ru.vs & WIN_MOD].cnt_pl_trans = 0;  /*first try to transmit*/
  grlc_data->ru.vs = (grlc_data->ru.vs+1) & 0x7F /*mod 128*/;
} /* ru_new_data() */




/*
+------------------------------------------------------------------------------
| Function    : ru_reorg_l1
+------------------------------------------------------------------------------
| Description : The function ru_reorg_l1() recalculates data blocks which are
|				passed to l1 but wasn´t sent due to usf only after succesfull 
|				contention resolution or change of coding scheme.
|
| Parameters  : sent_blks_i - description of parameter dummy
|
+------------------------------------------------------------------------------
*/
GLOBAL void ru_reorg_l1 ( UBYTE sent_blks_i )
{ 
  UBYTE cnt_reog_blks; /* number of reorged blks */
  UBYTE reorged_blks = 0; /*counts the number of modified blocks*/
  UBYTE i, bsn;

  TRACE_FUNCTION( "ru_reorg_l1" );


  cnt_reog_blks = grlc_data->ru.nts_max - sent_blks_i;

  for (i=0; i < cnt_reog_blks; i++)
  {
	  UBYTE index;

	  index = grlc_data->ru.pl_retrans_current.cnt - 1 - i;
	  bsn   = grlc_data->ru.pl_retrans_current.blk[index];
	  if(bsn <= BSN_MAX)
	  {	
#ifdef _SIMULATION_
    TRACE_EVENT_P1("bsn %d",bsn);
#endif
      if(!grlc_data->ru.rlc_data[bsn & WIN_MOD].cnt_pl_trans)
      {/* not transmitted over physical link */
        UBYTE cnt_data_parts;
        reorged_blks++;
        grlc_data->ru.vs--;
        grlc_data->ru.vs      = grlc_data->ru.vs & 0x7F;

        cnt_data_parts              =  grlc_data->ru.rlc_data[bsn & WIN_MOD].data_cnt - 1;
        grlc_data->ru.sdu_len       +=  (grlc_data->ru.rlc_data[bsn & WIN_MOD].data[cnt_data_parts].l_buf/8);
        grlc_data->ru.sdu_off        =  (grlc_data->ru.rlc_data[bsn & WIN_MOD].data[cnt_data_parts].o_buf/8);
        grlc_data->ru.rlc_octet_cnt +=  (grlc_data->ru.rlc_data[bsn & WIN_MOD].data[cnt_data_parts].l_buf/8);

        if(cnt_data_parts)
          grlc_data->ru.sdu_len = 0; /* pdu bound in block*/

        while(cnt_data_parts--)
        {
          grlc_data->ru.sdu_len       += (grlc_data->ru.rlc_data[bsn & WIN_MOD].data[cnt_data_parts].l_buf/8);
          grlc_data->ru.sdu_off        = grlc_data->ru.rlc_data[bsn & WIN_MOD].data[cnt_data_parts].o_buf/8;
          grlc_data->ru.rlc_octet_cnt += (grlc_data->ru.rlc_data[bsn & WIN_MOD].data[cnt_data_parts].l_buf/8);
          grlc_data->ru.next_prim       = grlc_data->ru.active_prim;
          grlc_data->ru.active_prim     = grlc_data->prim_queue[grlc_data->ru.active_prim].previous;
          grlc_data->prim_queue[grlc_data->ru.active_prim].last_bsn = grlc_data->prim_queue[grlc_data->ru.next_prim].last_bsn;
          TRACE_EVENT("parts of pdu present");
        }
        /*
         * handle pdu parametes
         */        
        if((grlc_data->ru.rlc_data[bsn & WIN_MOD].header.mac & 0x3C) EQ 0) /* countdown value is equal 0*/                    
        {
          grlc_data->ru.pdu_rem  += grlc_data->ru.rlc_data[bsn & WIN_MOD].data_cnt;
          grlc_data->ru.pdu_sent -= grlc_data->ru.rlc_data[bsn & WIN_MOD].data_cnt;
        }
        else if(grlc_data->ru.rlc_data[bsn & WIN_MOD].header.li_cnt AND
               ((grlc_data->ru.rlc_data[bsn & WIN_MOD].header.li_me[grlc_data->ru.rlc_data[bsn & WIN_MOD].header.li_cnt-1] & 0xFC) EQ 0)) 
        {
          grlc_data->ru.pdu_rem  += grlc_data->ru.rlc_data[bsn & WIN_MOD].data_cnt;
          grlc_data->ru.pdu_sent -= grlc_data->ru.rlc_data[bsn & WIN_MOD].data_cnt;
        }
        else
        {
          grlc_data->ru.pdu_rem  += grlc_data->ru.rlc_data[bsn & WIN_MOD].header.li_cnt;
          grlc_data->ru.pdu_sent -= grlc_data->ru.rlc_data[bsn & WIN_MOD].header.li_cnt;
        }
        if(grlc_data->ru.state EQ RU_REL_ACK)
        {
          SET_STATE(RU,RU_ACK);
        }
        else if(grlc_data->ru.state EQ RU_REL_UACK)
        {
          SET_STATE(RU,RU_UACK);
        }
        /*
         * Stop Countdown procedure, if bsn was the first block with CV NEQ 15 
         */
        if(grlc_data->ru.cd_active  AND
           (grlc_data->ru.rlc_data[((--bsn) & 0x7F) & WIN_MOD].header.mac & 0x3C) EQ 15)
        {
          TRACE_EVENT_P4("stop CNT DWN during reorg bsn-1 =%d bs_cv_max=%d,ru.cv=%d rlc_oc_ctn=%ld"
                                                           ,((bsn--) & 0x7F)
                                                           ,grlc_data->uplink_tbf.bs_cv_max
                                                           ,grlc_data->ru.cv
                                                           ,grlc_data->ru.rlc_octet_cnt);
          grlc_data->ru.cd_active = FALSE;
          ru_set_prim_queue(grlc_data->ru.cd_active);
        }

      }
      else
      {
        /*
         * block was once transmitted over physical link
         */
        reorged_blks++;
        /*
        TRACE_EVENT("block was transmitted over pl link, do not modify");
        */
      }
	  }
	  else
	  {
		  TRACE_EVENT("CTRL BLK in L1 queue: reorg not needed");
	  }
  }
  if(reorged_blks)
  {
    grlc_data->ru.write_pos_index       -= reorged_blks;
    grlc_data->ru.pl_retrans_current.cnt -= reorged_blks;
    memset(&grlc_data->ru.pl_retrans_current.blk[grlc_data->ru.pl_retrans_current.cnt],
           0xFF,
           reorged_blks);
  }
  ru_recalc_rlc_oct_cnt();

} /* ru_reorg_l1() */



/*
+------------------------------------------------------------------------------
| Function    : ru_del_prim_in_uack_mode
+------------------------------------------------------------------------------
| Description : The function ru_del_prim_in_uack_mode() deletes primitvies in 
|               rlc unackknowledged mode
|
| Parameters  : dummy - description of parameter dummy
|
+------------------------------------------------------------------------------
*/
GLOBAL void ru_del_prim_in_uack_mode ( UBYTE rlc_blocks_sent_i)
{ 
  UBYTE highest_bsn = BSN_MAX + 1; /* this represents the highest bsn sent on air */
  UBYTE  counter = 0;
  TRACE_FUNCTION( "ru_del_prim_in_uack_mode" );
  
  for(counter = rlc_blocks_sent_i;counter > 0;counter--)
  {
    
    highest_bsn = grlc_data->ru.pl_retrans_current.blk[counter-1];
    if( highest_bsn < (BSN_MAX + 1) )
    {
      /* data block has been transmitted on air, valid bsn found. */
      highest_bsn=(highest_bsn+1) & BSN_MAX;
      if(grlc_data->ru.cv NEQ 0)
      {
        ru_delete_prims(highest_bsn);
      }
      else if((grlc_data->ru.cv EQ 0) AND (grlc_data->ru.count_cv_0 EQ 4))
      {
        ru_delete_prims(highest_bsn);
      }
      break;
    }
    /* if highest_bsn is greater than BSN_MAX then it is a control block. we should check in 
     * next radio block
     */
  }
} /* ru_del_prim_in_uack_mode() */


/*
+------------------------------------------------------------------------------
| Function    : ru_handle_timers
+------------------------------------------------------------------------------
| Description : The function ru_handle_timers handles the following timers:
|                T3164, T3166, T3180, T3182, T3198
|
| Parameters  : dummy - description of parameter dummy
|
+------------------------------------------------------------------------------
*/
GLOBAL void ru_handle_timers ( UBYTE rlc_blocks_sent_i)
{
  UBYTE i;
  TRACE_FUNCTION( "ru_handle_timers" );

  /*
   * start timter T3198, l1 has transmitted a block
   */
  for(i=0 ; i < rlc_blocks_sent_i;i++)
  {
    if (grlc_data->ru.pl_retrans_current.blk[i] <= BSN_MAX )
      ru_set_T3198(grlc_data->ru.pl_retrans_current.blk[i]);
  }
  /*
   * first usf is read: stop T3164; wait for first ack/nack: start T3166
   */
  if( rlc_blocks_sent_i AND grlc_data->ru.first_usf)     
  {
    vsi_t_stop(GRLC_handle,T3164);
    grlc_data->ru.first_usf = FALSE;
    grlc_data->t3164_to_cnt = 0; 
  }
  if(grlc_data->ru.ti AND rlc_blocks_sent_i AND (!grlc_data->ru.cnt_ts))
  { 
    /* 
     * Only started in phase access when first data block was sent
     * in case of fixed allocation, after sending the first rlc data block
     * T3166 must be started(Wait for the first acknowledge).
     */
    vsi_t_start(GRLC_handle,T3166,T3166_VALUE);
  }
  /* 
   * start T3182 if at stall indication or
   * start T3182 if final data block was sent or
   * restart T3180 if a data block was sent, only in dynamic allocation
   */
  if (rlc_blocks_sent_i AND grlc_data->uplink_tbf.mac_mode EQ CGRLC_MAC_MODE_DA)
  {
    vsi_t_stop(GRLC_handle,T3180);
    vsi_t_start(GRLC_handle,T3180,T3180_VALUE);
  }
  if(    grlc_data->ru.last_bsn EQ LAST_BSN_STALL_CONDITION
     AND (grlc_t_status( T3182 ) EQ 0))
  {
    vsi_t_start(GRLC_handle,T3182,T3182_VALUE);
    TRACE_EVENT_P6("SI:T3182 is started: vs=%d va=%d dl_fn=%ld rlc_bs=%d bsn[0]=%d bsn[1]=%d"
                                                  ,grlc_data->ru.vs
                                                  ,grlc_data->ru.va
                                                  ,grlc_data->dl_fn
                                                  ,rlc_blocks_sent_i
                                                  ,grlc_data->ru.pl_retrans_current.blk[0]
                                                  ,grlc_data->ru.pl_retrans_current.blk[1]);
  }
  else if(    (grlc_data->ru.last_bsn EQ LAST_BSN_IS_SENT)
          AND (grlc_t_status( T3182 ) EQ 0) /* timer is not running */
          AND (!grlc_data->ru.nr_nacked_blks)) 
  {
    vsi_t_start(GRLC_handle,T3182,T3182_VALUE);
    TRACE_EVENT_P3("T3182 started : vs=%d va=%d  last_bsn=%d "
                                                        ,grlc_data->ru.vs
                                                        ,grlc_data->ru.va
                                                        ,grlc_data->ru.last_bsn);
  }
/*  else
  {
    TRACE_EVENT_P6("NO TIMER START: vs=%d va=%d  last_bsn=%d t3182=%ld t3180=%d rlc_blocks_sent_i=%d"
                                                        ,grlc_data->ru.vs
                                                        ,grlc_data->ru.va
                                                        ,grlc_data->ru.last_bsn
                                                        ,grlc_t_status( T3182 )
                                                        ,grlc_t_status( T3180 )
                                                        ,rlc_blocks_sent_i);
  }
  */
} /* ru_handle_timers() */




/*
+------------------------------------------------------------------------------
| Function    : ru_handle_nts
+------------------------------------------------------------------------------
| Description : The function ru_handle_nts recalutates the parameter 
|               grlc_data->ru.nts in case of layer 1 queue reorganisation
|
| Parameters  : dummy - description of parameter dummy
|
+------------------------------------------------------------------------------
*/

GLOBAL void ru_handle_nts (UBYTE rlc_blocks_sent_i)
{ 
  UBYTE i;
  TRACE_FUNCTION( "ru_handle_nts" );


  if( grlc_data->testmode.mode EQ CGRLC_LOOP      AND 
      (rlc_blocks_sent_i < grlc_data->ru.nts_max)     )
  {
    TRACE_EVENT_P2("TESTMODE B REORG L1: blk_sent = %d nts=nts_max=%d",rlc_blocks_sent_i,grlc_data->ru.nts_max);
    grlc_data->ru.reorg_l1_needed = TRUE; 
  }
  

  grlc_data->ru.nts = rlc_blocks_sent_i;
  
  /*
   * check whether there is a reorganisation of the tansmit queue needed
   * due to pending control blocks 
   */
  if( tm_get_num_ctrl_blck( ) NEQ 0 )
  {
    grlc_data->ru.reorg_l1_needed = TRUE;
  }


  if(   grlc_data->ru.reorg_l1_needed   
    AND (rlc_blocks_sent_i < grlc_data->ru.nts_max ) 
    AND grlc_data->ru.pl_retrans_current.cnt
    AND grlc_data->ru.pl_retrans_current.blk[0] NEQ 0xFF) /* dummy blocks in queue*/
  {    
    ru_reorg_l1(rlc_blocks_sent_i);
    grlc_data->ru.nts = grlc_data->ru.nts_max; 
  }
  else if (!grlc_data->ru.pl_retrans_current.cnt)
  { /* no data block in queue, put max nuber in queue*/
    grlc_data->ru.nts = grlc_data->ru.nts_max;
  }
  /********otherwise check if a positive acknowledged block is queue**********/
  else
  {
    for(i=0; i < grlc_data->ru.pl_retrans_current.cnt;i++)
    {
      if(grlc_data->ru.pl_retrans_current.blk[i] <= BSN_MAX AND 
         (grlc_data->ru.vb[grlc_data->ru.pl_retrans_current.blk[i] & WIN_MOD] EQ VB_ACKED))
      {
        grlc_data->ru.nts = (grlc_data->ru.nts_max -i);
        grlc_data->ru.pl_retrans_current.cnt -= (grlc_data->ru.nts - rlc_blocks_sent_i);
        memset(&grlc_data->ru.pl_retrans_current.blk[i],
             0xFF,
             grlc_data->ru.nts);
        grlc_data->ru.write_pos_index -= grlc_data->ru.nts - rlc_blocks_sent_i;
        break;
      }
    }
  }
  /***********************check if a bsn_ret block is in queue*****************/ 
  for(i=0; i < grlc_data->ru.pl_retrans_current.cnt;i++)
  {
    if(grlc_data->ru.pl_retrans_current.blk[i] EQ grlc_data->ru.bsn_ret)
    {
      grlc_data->ru.bsn_ret = ru_set_next_bsn_ret();    
    }
  }
  /****************************************************************************/ 
  grlc_data->ru.reorg_l1_needed = FALSE;



} /* ru_handle_nts() */


/*
+------------------------------------------------------------------------------
| Function    : ru_check_pl_ret
+------------------------------------------------------------------------------
| Description : handles the restart of timer T3198, and first call of RU 
|               (to stop T3164)
|
| Parameters  : rlc_blocks_sent_i - number of needed blocks by PL(is equal to 
|               the number of sent blocks in the previous radio block)
|
+------------------------------------------------------------------------------
*/
GLOBAL void ru_check_pl_ret ( UBYTE rlc_blocks_sent_i)
{
  UBYTE i;
  UBYTE bsn;

  TRACE_FUNCTION( "ru_check_pl_ret" );

    
  /*
   * handle the sent blocks
   */
  for(i=0;i<rlc_blocks_sent_i;i++)
  {
	  bsn = grlc_data->ru.pl_retrans_current.blk[i];
    /*
     * chek if the range is OK
     */
    if(  bsn <= BSN_MAX )          /* data block was sent */
    {  
      /*
       * trace parametes: 1. retranmission counter, byte counter 
       */
      grlc_data->ru.rlc_data[bsn & WIN_MOD].cnt_pl_trans++;
      if(grlc_data->ru.rlc_data[bsn & WIN_MOD].cnt_pl_trans > 1) 
        grlc_data->tbf_ctrl[grlc_data->ul_index].ret_bsn++;
      else
      {
        UBYTE j;
        for(j=0;j<grlc_data->ru.rlc_data[bsn & WIN_MOD].data_cnt;j++)
          grlc_data->tbf_ctrl[grlc_data->ul_index].rlc_oct_cnt +=grlc_data->ru.rlc_data[bsn & WIN_MOD].data[j].l_buf/8;
      }
      /* 
       * a retransmitted block was sent 
       */
      if(bsn EQ grlc_data->ru.bsn_ret)
      {
        grlc_data->ru.bsn_ret = ru_set_next_bsn_ret(); 
      }
      /*
       * last bsn is sent
       */
      if((grlc_data->ru.rlc_data[bsn & WIN_MOD].header.mac & 0x3C) EQ 0) /* countdown value is equal 0*/ 
      {
        grlc_data->ru.last_bsn = LAST_BSN_IS_SENT;
      }
      else if(bsn EQ ((grlc_data->ru.va+WIN_SIZE-1) & 0x7F)
              AND (grlc_data->ru.last_bsn NEQ LAST_BSN_STALL_CONDITION))
      {
        /*
         * stall indication detected
         */
        grlc_data->ru.last_bsn = LAST_BSN_STALL_CONDITION;
        TRACE_EVENT_P6("NEXT DATA STALL INDICATION bsn=%d va=%d vs=%d  cnt_ts=%d last_bsn=%ld dl_fn=%ld"
                                                          ,bsn
                                                          ,grlc_data->ru.va
                                                          ,grlc_data->ru.vs
                                                          ,grlc_data->ru.cnt_ts
                                                          ,grlc_data->ru.last_bsn
                                                          ,grlc_data->dl_fn);
      }
    }
    else if( bsn >= OFFSET_CTRL_BLOCK_IDX AND bsn < 0xFF ) /* control block was sent */
    {
      sig_ru_tm_ctrl_blk_sent( (UBYTE)( bsn - OFFSET_CTRL_BLOCK_IDX ) );
    } 
    else if (bsn EQ 0xFF)
    {
      /* nothing to do : dummy block sent*/
    }
    else /*invalid block was sent, should not appear */
    {
      TRACE_ERROR("INVALID bsn range neither data or control block");
      TRACE_EVENT("INVALID bsn range neither data or control block");
      TRACE_EVENT_P1("bsn = %d ", bsn);
    }    
  }
  ru_handle_timers(rlc_blocks_sent_i);
  /******************delete sent blocks from pl_retrans_current**************/
  memcpy(grlc_data->ru.pl_retrans_current.blk,
          &grlc_data->ru.pl_retrans_current.blk[rlc_blocks_sent_i],
           (grlc_data->ru.nts_max - rlc_blocks_sent_i));
  grlc_data->ru.pl_retrans_current.cnt -= rlc_blocks_sent_i;
  grlc_data->ru.cnt_ts += rlc_blocks_sent_i;

} /* ru_check_pl_ret() */






/*
+------------------------------------------------------------------------------
| Function    : ru_send_ul_dummy_block
+------------------------------------------------------------------------------
| Description : The function ru_send_ul_dummy_block() sends a uplink dummy 
|               control block instead of RLC data block
|
| Parameters  : dummy - description of parameter dummy
|
+------------------------------------------------------------------------------
*/
GLOBAL void ru_send_ul_dummy_block ( void )
{ 
  MCAST (ul_dummy,U_GRLC_UL_DUMMY);

  TRACE_FUNCTION( "ru_send_ul_dummy_block" );


  memset(&grlc_data->ru.ul_data[grlc_data->ru.write_pos_index],
          0, 
          sizeof(T_ul_data));

  grlc_data->ru.ul_data[grlc_data->ru.write_pos_index].block_status = 2;

  /*
   * set uplink dummy block
   */
  ul_dummy->msg_type = U_GRLC_UL_DUMMY_c;

  grlc_set_buf_tlli( &ul_dummy->tlli_value, grlc_data->uplink_tbf.tlli );
  grlc_encode_ul_ctrl_block( ( UBYTE* ) grlc_data->ru.ul_data[grlc_data->ru.write_pos_index].ul_block,
                            ( UBYTE* )ul_dummy );

#ifdef _SIMULATION_
  {
    PALLOC(mac_data_req,MAC_DATA_REQ);
    memset(&(mac_data_req->ul_data),
           0, 
           sizeof(T_ul_data));
    memcpy(&(mac_data_req->ul_data),
           &(grlc_data->ru.ul_data[grlc_data->ru.write_pos_index]),
           sizeof(T_ul_data));
    PSEND(hCommL1,mac_data_req);
  }
  TRACE_EVENT_P1("wpi %d",grlc_data->ru.write_pos_index);
#else  /* #ifdef _SIMULATION_ */
  {
    TRACE_MEMORY_PRIM ( hCommGRLC, hCommL1, MAC_DATA_REQ,
                        &grlc_data->ru.ul_data[grlc_data->ru.write_pos_index],
                        sizeof(T_ul_data) );
  }
#endif /* #ifdef _SIMULATION_ */

  {
    UBYTE* ul_block = ( UBYTE* )grlc_data->ru.ul_data[grlc_data->ru.write_pos_index].ul_block;
  
    TRACE_BINDUMP( hCommGRLC, TC_USER5,
                   cl_rlcmac_get_msg_name
                     ( ( UBYTE )( ul_block[1] >> 2 ), RLC_MAC_ROUTE_UL ),
                   ul_block, MAX_L2_FRAME_SIZE ); /*lint !e569*/
  }

  grlc_data->ru.write_pos_index++;
  grlc_data->ru.ul_data[grlc_data->ru.write_pos_index].block_status = 0;

  grlc_data->ru.pl_retrans_current.blk[grlc_data->ru.pl_retrans_current.cnt] = 255;
  grlc_data->ru.pl_retrans_current.cnt++;
  grlc_data->ru.nts--;  
} /* ru_send_ul_dummy_block() */




/*
+------------------------------------------------------------------------------
| Function    : ru_handle_stall_ind
+------------------------------------------------------------------------------
| Description : The function ru_handle_stall_ind() checks, if there was a stall
|               condition, and if it is canceled.
|
| Parameters  : dummy - description of parameter dummy
|
+------------------------------------------------------------------------------
*/
GLOBAL void ru_handle_stall_ind ( void )
{ 
  TRACE_FUNCTION( "ru_handle_stall_ind" );

  if(grlc_data->ru.last_bsn EQ LAST_BSN_STALL_CONDITION)
  {
    if(!(grlc_data->ru.vs EQ ((grlc_data->ru.va + WIN_SIZE) & 0x7F)))
    {/*stall condition eliminated*/
      grlc_data->ru.reorg_l1_needed = TRUE;  /* remove stalled blocks from data queue */      
      vsi_t_stop(GRLC_handle,T3182);
      ru_handle_n3102(PAN_INC);
      if(grlc_data->ru.rlc_mode EQ CGRLC_RLC_MODE_UACK)
      { /*resume with data transfer after stall indication in rlc unacknowledged problem*/
        grlc_data->ru.last_bsn = LAST_BSN_RESUME_UACK_MODE_AFTER_SI; 
      } 
      else
      {
        grlc_data->ru.last_bsn = LAST_BSN_NOT_BULIT;
      }
    }
  }
  else
  {
    ru_handle_n3102(PAN_INC);
    grlc_data->ru.last_si_block = grlc_data->ru.va;
  }
} /* ru_handle_stall_ind() */


/*
+------------------------------------------------------------------------------
| Function    : ru_handle_tbf_start_in_ptm
+------------------------------------------------------------------------------
| Description : The function ru_handle_tbf_start_in_ptm() modifies the parameters
|               for the tbf at reaching the starting time
|
| Parameters  : rlc_blocks_sent_i - number of blocks sent in current in radio block
|               return value is set, if only nts is decrased and not all blocks are sent
|
+------------------------------------------------------------------------------
*/
GLOBAL void ru_handle_tbf_start_in_ptm ( UBYTE rlc_blocks_sent_i )
{
  UBYTE  ctrl_blk_active_idx;
  BOOL   realloc_prr_allowed = FALSE;
  TRACE_FUNCTION( "ru_handle_tbf_start_in_ptm" );
  /*
   * reassignment of uplink and tbf starting time elapsed, received either with packet uplink assignment
   * or packet timeslot reconfigure
   */

  
  if(grlc_data->ul_tfi_changed)
  {
    if((rlc_blocks_sent_i < grlc_data->ru.nts_max)  AND                    /* nr of locks which are not sent */
       (grlc_data->ru.cs_type EQ grlc_data->ru.next_tbf_params.cs_type) AND /* coding scheme has not changed  */
       (grlc_data->ru.nts_max <= grlc_data->ru.next_tbf_params.nts))        /* nts increased or equal */
    {
      UBYTE i, bsn[4],cnt;
      /* 
       * change the tfi for blocks which are in l1 queue 
       */
      cnt    = grlc_data->ru.pl_retrans_current.cnt;
      bsn[0] = grlc_data->ru.pl_retrans_current.blk[0];
      bsn[1] = grlc_data->ru.pl_retrans_current.blk[1];
      grlc_data->ru.pl_retrans_current.cnt = 0;
      for (i = 0; i< cnt;i++)
      {
        if(bsn[i] <= BSN_MAX)
        {
          grlc_data->ru.write_pos_index--;      
          ru_send_mac_data_req(bsn[i]);
          TRACE_EVENT_P1("bsn %d modified while tfi is changed",bsn[i]);
        }  
        else
        {
          TRACE_EVENT_P1("bsn %d :TFI FOR CTRL BLOCK CHANGED. no modification",bsn[i]);
        }
      }
    }
    grlc_data->ul_tfi_changed  = FALSE;
  }



  grlc_data->ru.tlli_cs_type = grlc_data->ru.next_tbf_params.tlli_cs_type;
  if(grlc_data->ru.cs_type NEQ grlc_data->ru.next_tbf_params.cs_type)
  {
    TRACE_EVENT_P2("UL assign: CS changed from %d to %d ",grlc_data->ru.cs_type,grlc_data->ru.next_tbf_params.cs_type);
    grlc_data->ru.cs_type      = grlc_data->ru.next_tbf_params.cs_type;
    ru_change_of_cs(grlc_data->ru.cs_type);        
  }
  if(grlc_data->ru.nts_max < grlc_data->ru.next_tbf_params.nts)
  {
    TRACE_EVENT_P6("nts increased from %d to %d, tfi=%d,cnt_ts=%d,vs=%d,va=%d",
                                      grlc_data->ru.nts_max,
                                      grlc_data->ru.next_tbf_params.nts,
                                      grlc_data->ul_tfi,
                                      grlc_data->ru.cnt_ts,
                                      grlc_data->ru.vs,
                                      grlc_data->ru.va);
    grlc_data->ru.nts = (grlc_data->ru.next_tbf_params.nts - grlc_data->ru.nts_max);
    grlc_data->ru.nts_max = grlc_data->ru.next_tbf_params.nts;

    while( grlc_data->ru.nts              AND
           tm_get_num_ctrl_blck( ) NEQ 0  AND 
           ru_ctrl_blk_selection_allowed() )

    { /* 
       * next uplink block is a control block, 
       *  check if countdown procedure is statred or not
       */
      ru_send_control_block( );
    }
    while(grlc_data->ru.nts)
    { 
      ctrl_blk_active_idx = ru_peek_for_ctrl_blk();
      if ((ctrl_blk_active_idx EQ 0xFF) OR
           realloc_prr_allowed EQ TRUE)     /*No control block , form data block*/
      { 
        while(grlc_data->ru.nts AND grlc_data->ru.sdu_len)
        {
          ru_new_data();
        }
        while(grlc_data->ru.nts)
        {
          ru_ret_bsn();
        }
        realloc_prr_allowed = FALSE;
        break;
      }
      else
      {
        
        TRACE_EVENT_P1("reallocation of llc pdu (index)=%d",ctrl_blk_active_idx);
        
        /* if already one PRR in L1 Buffer , replace it with new PRR */
        if (grlc_data->ru.pl_retrans_current.blk[0]
          EQ (BLK_INDEX_TM + OFFSET_CTRL_BLOCK_IDX))
        {      
          grlc_data->ru.write_pos_index--;
          grlc_data->ru.pl_retrans_current.cnt--;
          grlc_data->ru.nts++;  
          TRACE_EVENT("prr in l1 buffer queue,replace with new prr");
        }
        sig_ru_tm_end_of_pdu(ctrl_blk_active_idx);    
        ru_send_control_block();    
        realloc_prr_allowed = TRUE;      
      }
    }
    if(grlc_data->ru.cv EQ 0 AND
       grlc_data->ru.rlc_mode EQ CGRLC_RLC_MODE_ACK )
    {
      SET_STATE(RU,RU_REL_ACK);
    }
    else if(grlc_data->ru.rlc_mode EQ CGRLC_RLC_MODE_ACK)
    {
      SET_STATE(RU,RU_ACK);
    }
    else if(grlc_data->ru.cv EQ 0 AND
       grlc_data->ru.rlc_mode EQ CGRLC_RLC_MODE_UACK )
    {
      SET_STATE(RU,RU_REL_UACK);
    }
    else if(grlc_data->ru.rlc_mode EQ CGRLC_RLC_MODE_UACK)
    {
      SET_STATE(RU,RU_UACK);
    }
    else
    {
      TRACE_EVENT_P2("RLC MODE ??? cv=%d rlc_mode=%d",grlc_data->ru.cv,grlc_data->ru.rlc_mode);
    }
  }
  else if(grlc_data->ru.nts_max > grlc_data->ru.next_tbf_params.nts)
  {
    /* Reorg layer 1 */
    ru_reorg_l1((UBYTE)(rlc_blocks_sent_i));   
    TRACE_EVENT_P7("nts decrased from %d to %d tfi=%d,cnt_ts=%d,va=%d,vs=%d blk_s=%d  ",
                                                                grlc_data->ru.nts_max,
                                                                grlc_data->ru.next_tbf_params.nts,
                                                                grlc_data->ul_tfi,
                                                                grlc_data->ru.cnt_ts,
                                                                grlc_data->ru.va,
                                                                grlc_data->ru.vs,
                                                                rlc_blocks_sent_i);
    grlc_data->ru.nts_max = grlc_data->ru.next_tbf_params.nts;
  }
  else
  {
    TRACE_EVENT_P6("ul tbf reassignment with tfi=%d,cnt_ts=%d,va=%d,vs=%d,nts_max=%d tbf_st_time=%ld",
                                                                grlc_data->ul_tfi,
                                                                grlc_data->ru.cnt_ts,
                                                                grlc_data->ru.va,
                                                                grlc_data->ru.vs,
                                                                grlc_data->ru.nts_max,
                                                                grlc_data->ul_tbf_start_time);
  }
  grlc_data->ru.v_next_tbf_params = FALSE;
  
} /* ru_handle_tbf_start_in_ptm() */


/*
+------------------------------------------------------------------------------
| Function    : ru_switch_ul_buffer 
+------------------------------------------------------------------------------
| Description : The function ru_switch_ul_buffer () .... 
|
| Parameters  : -
|
+------------------------------------------------------------------------------
*/
GLOBAL void ru_switch_ul_buffer  ( UBYTE rlc_blocks_sent_i )
{ 

  UBYTE i;

  TRACE_FUNCTION( "ru_switch_ul_buffer " );

  /*
   * switch uplink buffer, it means, rlc blocks which are not sent
   * by PL are switched to the top of the ul buffer
   */
  if (rlc_blocks_sent_i)
  {
    i = rlc_blocks_sent_i;
    while((i < MAX_UL_TN) AND (grlc_data->ru.ul_data[i].block_status NEQ 0))
    {
      grlc_data->ru.ul_data[i - rlc_blocks_sent_i] = grlc_data->ru.ul_data[i]; 
      i++;
    }
    grlc_data->ru.write_pos_index -= rlc_blocks_sent_i;
  } 
} /* ru_switch_ul_buffer () */

/*
+------------------------------------------------------------------------------
| Function    : ru_cgrlc_st_time_ind 
+------------------------------------------------------------------------------
| Description : The function ru_cgrlc_st_time_ind () informs higher layers
|               that the starting time is elapsed
|
| Parameters  : -
|
+------------------------------------------------------------------------------
*/
GLOBAL void ru_cgrlc_st_time_ind  ( void )
{ 
  PALLOC(cgrlc_starting_time_ind,CGRLC_STARTING_TIME_IND); /* T_CGRLC_STARTING_TIME_IND */

  TRACE_FUNCTION( "ru_cgrlc_st_time_ind " );

  cgrlc_starting_time_ind->tbf_mode = CGRLC_TBF_MODE_UL;
  cgrlc_starting_time_ind->tfi      = grlc_data->ul_tfi;
 
  PSEND(hCommGRR,cgrlc_starting_time_ind);

  grlc_data->ul_tn_mask = grlc_data->uplink_tbf.ts_mask;
  

} /* ru_cgrlc_st_time_ind () */

/*
+------------------------------------------------------------------------------
| Function    : ru_ctrl_blk_selection_allowed
+------------------------------------------------------------------------------
| Description : This function gets called from sig_gff_ru_mac_ready_ind handler 
|               and also from the function which handles ul reassignment
|               (ru_handle_tbf_start_in_ptm). In mac ready indicate handler 
|               this is calld only when ru is in rel_ack state. 
|               This function returns true or false according to the following
|               table. The table applies for uack mode also.
|               When this function returns true, then control block should be
|               selected for transmission by RU.
|               When it returns False, then control block is selected by RD.
|               
|                  ru_rel_ack state          rd_rel_ack         - TRUE
|                  ru_ack                    rd_ack             - TRUE
|                  ru_ack                    rd_rel_ack         - TRUE
|                  ru_rel_ack                rd_ack             - FALSE
|                  ru_ack                    NO DL TBF(rd_null) - TRUE
|                  ru_rel_ack                NO DL TBF(rd_null) - TRUE
|               
|               This function should be called only in PTM.
|
| Parameters  : None
+------------------------------------------------------------------------------
*/
GLOBAL BOOL ru_ctrl_blk_selection_allowed()
{
  BOOL ru_in_release_mode = FALSE, ctrl_blk_allowed = TRUE, dl_release_started = TRUE;

  if(grlc_data->tbf_type EQ TBF_TYPE_CONC)
  {
    /* In ru_send_pca stete, ctrl blocks are not sent. No need to 
     * check for that here.
     */
    if((GET_STATE(RU) EQ RU_REL_ACK) OR (GET_STATE(RU) EQ RU_REL_UACK))
    {
      ru_in_release_mode = TRUE;
    }
  
    sig_ru_rd_get_downlink_release_state(&dl_release_started);
     
    /* rd_rel_state would be true if fbi=1 has been received
     * in downlink.
     */
    if(ru_in_release_mode AND (dl_release_started EQ FALSE) )
    {
      ctrl_blk_allowed = FALSE;
    }
  }
  return ctrl_blk_allowed;
}

/*
+------------------------------------------------------------------------------
| Function    : ru_peek_for_ctrl_blk
+------------------------------------------------------------------------------
| Description : The function would peek to see if there is reallocation set,
|               then it returns TRUE, so that PRR can be constructed. 
|               Also if the rlc block has more than one llc pdu with re-allocation
|               set , then prr os sent for the latest llc pdu. 
| Parameters  : None.
|
+------------------------------------------------------------------------------
*/

#ifndef CF_FAST_EXEC

GLOBAL UBYTE ru_peek_for_ctrl_blk()
{
  BOOL    ctrlblk_found = FALSE; 
  USHORT  rlc_len;
  USHORT  sdu_len;
  UBYTE   active_prim,next_prim,active_prr_idx = 0xFF;

  TRACE_FUNCTION( "ru_peek_for_ctrl_blk" );
  
  sdu_len     = grlc_data->ru.sdu_len;
  active_prim = grlc_data->ru.active_prim;
  next_prim   = grlc_data->ru.next_prim;
  rlc_len     = grlc_data->ru.rlc_data_size;
  
  
  while ( (sdu_len < rlc_len)  AND  (sdu_len > 0) )
  {  
    rlc_len -= sdu_len + 1; /* 1 is for length indicator */    
    
    if (!grlc_data->ru.cd_active)
    {
      ctrlblk_found = ru_peek_next_sdu(&sdu_len,&active_prim,&next_prim);
      active_prr_idx = ctrlblk_found ? active_prim:0xFF;
    }
    else
    {
#ifdef _SIMULATION_
      TRACE_EVENT("countdown in progress and extended tbf not supported,cant build PRR");
#endif
      return active_prr_idx;
    }        
 
  }   
  
  return (ctrlblk_found ? active_prr_idx : 0xFF);
}/*ru_peek_for_ctrl_blk*/
#endif /* CF_FAST_EXEC */


/*
+------------------------------------------------------------------------------
| Function    : ru_peek_next_sdu
+------------------------------------------------------------------------------
| Description : The function would peek to see if there is reallocation set,
|               for the current llc PDU.
|               
| Parameters  : None.
|
+------------------------------------------------------------------------------
*/
#ifndef CF_FAST_EXEC

GLOBAL BOOL ru_peek_next_sdu(USHORT *sdu_len,UBYTE *active_prim,UBYTE *next_prim)
{   
  BOOL prr_build = FALSE;

  TRACE_FUNCTION( "ru_peek_next_sdu");  
  
  if( *next_prim NEQ 0xFF  AND
     (grlc_data->prim_queue[*next_prim].start_new_tbf EQ 0))
  {
    *active_prim = grlc_data->prim_queue[*active_prim].next;
    /*  This signal allows TM to initiate resource re-allocation
    *  if required
    */
    prr_build = grlc_data->prim_queue[*active_prim].re_allocation;          
    
    *sdu_len  = grlc_data->prim_queue[*active_prim].prim_ptr->sdu.l_buf/8;
    *next_prim = grlc_data->prim_queue[*active_prim].next;  
  }
  else
  {
   *sdu_len  = 0;   
  }  
  return prr_build;
}/*ru_peek_next_sdu*/
#endif /* CF_FAST_EXEC */