view src/g23m-gprs/grlc/grlc_rds.c @ 554:28ed4002131a

configs/* except classic and hybrid-gpf: all parts of TCS211 GPF for which we found exact corresponding sources are now rebuilt from those sources; only OSL and OSX remain as blobs, plus CCD for non-hybrid configs
author Mychaela Falconia <falcon@freecalypso.org>
date Mon, 19 Nov 2018 00:12:20 +0000
parents 219afcfc6250
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 signal handler functions for service
|             RD of entity GRLC.
+----------------------------------------------------------------------------- 
*/ 

#ifndef GRLC_RDS_C
#define GRLC_RDS_C
#endif

#define ENTITY_GRLC

/*==== INCLUDES =============================================================*/
#include <string.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_rdf.h"
#include "grlc_tms.h"
#include "grlc_f.h"
#include "grlc_meass.h"

/*==== CONST ================================================================*/
const T_TIME T3192_Values[8] = {500, 1000, 1500, 1, 80, 120, 160, 200};

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

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

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



/*
+------------------------------------------------------------------------------
| Function    : sig_tm_rd_assign
+------------------------------------------------------------------------------
| Description : Handles the internal signal SIG_TM_RD_ASSIGN
|
| Parameters  : dummy - description of parameter dummy
|
+------------------------------------------------------------------------------
*/
GLOBAL void sig_tm_rd_assign ( void ) 
{ 
  TRACE_ISIG( "sig_tm_rd_assign" );
  
  switch( GET_STATE( RD ) )
  {
    case RD_WAIT_FOR_STARTING_TIME_ACK:
    case RD_WAIT_FOR_STARTING_TIME_UACK:
      TRACE_EVENT_P3("DL reassignment during starting time :%ld: dl_tfi=%d, st_dl_tfi=%d"
                                                                ,grlc_data->dl_tbf_start_time
                                                                ,grlc_data->dl_tfi
                                                                ,grlc_data->start_fn_dl_tfi);


      /*lint -fallthrough*/

    case RD_NULL:            
      rd_tbf_init();
      if(grlc_data->rd.rlc_mode EQ CGRLC_RLC_MODE_ACK)
      {
        SET_STATE(RD,RD_WAIT_FOR_STARTING_TIME_ACK);
      }
      else
      {
        SET_STATE(RD,RD_WAIT_FOR_STARTING_TIME_UACK);
      }    
      grlc_data->dl_index = 1;
      grlc_data->tbf_ctrl[grlc_data->dl_index].tbf_type    = TBF_TYPE_DL;
      grlc_data->tbf_ctrl[grlc_data->dl_index].tfi         = grlc_data->dl_tfi;
      grlc_data->tbf_ctrl[grlc_data->dl_index].pdu_cnt     = 0;
      grlc_data->tbf_ctrl[grlc_data->dl_index].rlc_oct_cnt = 0;
      grlc_data->tbf_ctrl[grlc_data->dl_index].cnt_ts      = 0;
      grlc_data->tbf_ctrl[grlc_data->dl_index].ack_cnt     = 0;
      grlc_data->tbf_ctrl[grlc_data->dl_index].fbi         = 0;
      grlc_data->tbf_ctrl[grlc_data->dl_index].ret_bsn     = 0;

      grlc_data->rd.ack_ctrl.cnt_meas_rpt                 = ACK_CNT_MEAS_RPT_FIRST;
      grlc_data->rd.ack_ctrl.cnt_other                    = ACK_CNT_NORMAL;
      break;
    case RD_ACK:
    case RD_UACK:
      vsi_t_stop(GRLC_handle,T3190); 
      vsi_t_start(GRLC_handle,T3190,T3190_VALUE);
      grlc_data->rd.next_tbf_params   = grlc_data->downlink_tbf;
      grlc_data->rd.v_next_tbf_params = TRUE;
      TRACE_EVENT_P3("dl tbf reassignment with tfi %d, nts=%d tbf_st_time=%ld"
                                              ,grlc_data->dl_tfi
                                              ,grlc_data->downlink_tbf.nts
                                              ,grlc_data->dl_tbf_start_time);
      break;
    default:
      TRACE_ERROR( "SIG_TM_RD_ASSIGN unexpected" );
      {
        TRACE_EVENT_P2("DL ASS: state=%d  t3192=%d "
                                                                        ,grlc_data->rd.state
                                                                        ,grlc_data->downlink_tbf.t3192_val );
      }
      break;
  }
} /* sig_tm_rd_assign() */



/*
+------------------------------------------------------------------------------
| Function    : sig_tm_rd_abrel
+------------------------------------------------------------------------------
| Description : Handles the internal signal SIG_TM_RD_ABREL
|
| Parameters  : dummy - description of parameter dummy
|
+------------------------------------------------------------------------------
*/
GLOBAL void sig_tm_rd_abrel ( ULONG fn, BOOL poll) 
{ 
  TRACE_ISIG( "sig_tm_rd_abrel" );
  
  switch( GET_STATE( RD ) )
  {
    case RD_ACK:
    case RD_REL_ACK:
    case RD_UACK:
    case RD_REL_UACK:
    case RD_NET_REL:    
    case RD_WAIT_FOR_STARTING_TIME_ACK:
    case RD_WAIT_FOR_STARTING_TIME_UACK:
      vsi_t_stop(GRLC_handle,T3190);
      vsi_t_stop(GRLC_handle,T3192);
	    
      rd_free_desc_list_partions();
      rd_free_database_partions();

      grlc_data->tbf_ctrl[grlc_data->dl_index].vs_vr  = grlc_data->rd.vr;
      grlc_data->tbf_ctrl[grlc_data->dl_index].va_vq  = grlc_data->rd.vq;
      grlc_trace_tbf_par ( grlc_data->dl_index );
       
      if(!poll)
      {
        /*
         * abnormal release: abort TBF
         */
        SET_STATE(RD,RD_NULL);          
      }
      else
      {
        SET_STATE(RD,RD_NET_REL);
        grlc_data->rd.fn_p_tbf_rel = fn;
        TRACE_EVENT_P2("p. tbf rel c_fn=%ld poll_fn=%d",fn,grlc_data->rd.fn_p_tbf_rel);
      }
      break;
    default:
      TRACE_ERROR( "SIG_TM_RD_ABREL unexpected" );
      break;
  }
} /* sig_tm_rd_abrel() */

/*
+------------------------------------------------------------------------------
| Function    : sig_tm_rd_ul_req
+------------------------------------------------------------------------------
| Description : Handles the internal signal SIG_TM_RD_UL_REQ
|
| Parameters  : dummy - description of parameter dummy
|
+------------------------------------------------------------------------------
*/
GLOBAL void sig_tm_rd_ul_req ( void) 
{ 
  TRACE_ISIG( "sig_tm_rd_ul_req" );
  
  switch( GET_STATE( RD ) )
  {
    case RD_ACK:
    case RD_REL_ACK:
    case RD_UACK:
    case RD_REL_UACK:
    case RD_WAIT_FOR_STARTING_TIME_ACK:
    case RD_WAIT_FOR_STARTING_TIME_UACK:
      grlc_data->rd.channel_req = TRUE;      
      break;
    default:
      TRACE_ERROR( "SIG_TM_RD_UL_REQ unexpected" );
      break;
  }
} /* sig_tm_rd_ul_req() */


/*
+------------------------------------------------------------------------------
| Function    : sig_tm_rd_ul_req_stop
+------------------------------------------------------------------------------
| Description : Handles the internal signal SIG_TM_RD_UL_REQ_STOP: No packet
|               channel description shall be sent in packet downlink ack/nack,
|               because a valid packet uplink assignment is received
|
| Parameters  : dummy - description of parameter dummy
|
+------------------------------------------------------------------------------
*/
GLOBAL void sig_tm_rd_ul_req_stop ( void) 
{ 
  TRACE_ISIG( "sig_tm_rd_ul_req_stop" );
  /*
   * do it in every state
   */  
  grlc_data->rd.channel_req = FALSE;

} /* sig_tm_rd_ul_req_stop() */

/*
+------------------------------------------------------------------------------
| Function    : sig_tm_rd_nor_rel
+------------------------------------------------------------------------------
| Description : Handles the internal signal SIG_TM_RD_NOR_REL
|
| Parameters  : dummy - description of parameter dummy
|
+------------------------------------------------------------------------------
*/
GLOBAL void sig_tm_rd_nor_rel ( void) 
{ 
  TRACE_ISIG( "sig_tm_rd_nor_rel" );
  
  switch( GET_STATE( RD ) )
  {
    case RD_NULL:
    case RD_ACK:
    case RD_REL_ACK:
    case RD_UACK:
    case RD_REL_UACK:
    case RD_NET_REL:  
    case RD_WAIT_FOR_STARTING_TIME_ACK:
    case RD_WAIT_FOR_STARTING_TIME_UACK:
      SET_STATE(RD,RD_NULL);
      vsi_t_stop(GRLC_handle,T3190);
      vsi_t_stop(GRLC_handle,T3192);	  
      grlc_data->tbf_ctrl[grlc_data->dl_index].vs_vr  = grlc_data->rd.vr;
      grlc_data->tbf_ctrl[grlc_data->dl_index].va_vq  = grlc_data->rd.vq; 
      grlc_trace_tbf_par ( grlc_data->dl_index );
      break;
    default:
      TRACE_ERROR( "SIG_TM_RD_NOR_REL unexpected" );
      break;
  }
} /* sig_tm_rd_nor_rel() */





/*
+------------------------------------------------------------------------------
| Function    : sig_gff_rd_mac_ready_ind
+------------------------------------------------------------------------------
| Description : Handles the Signal sig_gff_rd_mac_ready_ind
|
| Parameters  : *mac_dl_ready_ind - Ptr to primitive MAC_READY_IND
|
+------------------------------------------------------------------------------
*/
GLOBAL void sig_gff_rd_mac_ready_ind ( T_MAC_READY_IND * mac_dl_ready_ind)
{
  UBYTE   *ptr_block=NULL;
  ULONG   delta_fn;
   
  TRACE_ISIG( "sig_gff_rd_mac_ready_ind" );


  grlc_data->tbf_ctrl[grlc_data->dl_index].end_fn = mac_dl_ready_ind->fn;  

  grlc_handle_poll_pos (mac_dl_ready_ind->fn);
  delta_fn = rd_calc_delta_fn(mac_dl_ready_ind->fn);

  switch( GET_STATE( RD ) )
  {
    case RD_WAIT_FOR_STARTING_TIME_ACK:
    case RD_WAIT_FOR_STARTING_TIME_UACK:       
      if(!grlc_check_if_tbf_start_is_elapsed ( grlc_data->dl_tbf_start_time, mac_dl_ready_ind->fn))
      {
        TRACE_EVENT_P2("WAIT FOR DL STARTING TIME, UL IS RUNNING st=%ld fn=%ld",grlc_data->dl_tbf_start_time
                                                                              ,mac_dl_ready_ind->fn);
        grlc_send_rem_poll_pos (mac_dl_ready_ind->fn);
        return;
      }
      if(grlc_data->rd.rlc_mode EQ CGRLC_RLC_MODE_ACK)
      {
        SET_STATE(RD,RD_ACK);
      }
      else
      {
        SET_STATE(RD,RD_UACK);
      } 
      grlc_data->tbf_ctrl[grlc_data->dl_index].start_fn    = mac_dl_ready_ind->fn;
      
      TRACE_EVENT_P6("DL first call at fn = %ld (%ld) with tfi=%d, dl_mask=%x tbf_st_time=%ld ta=%d",
                                        mac_dl_ready_ind->fn,
                                        mac_dl_ready_ind->fn%42432,
                                        grlc_data->dl_tfi,
                                        grlc_data->dl_tn_mask,
                                        grlc_data->dl_tbf_start_time,
                                        grlc_data->ta_value);
      rd_cgrlc_st_time_ind();
      vsi_t_start(GRLC_handle,T3190,T3190_VALUE);

      /*lint -fallthrough*/

    case RD_ACK:
    case RD_REL_ACK:
    case RD_UACK:
    case RD_REL_UACK:
      if(grlc_data->rd.v_next_tbf_params AND
          grlc_check_if_tbf_start_is_elapsed ( grlc_data->dl_tbf_start_time, mac_dl_ready_ind->fn))
      {
        grlc_data->rd.v_next_tbf_params = FALSE;
        rd_cgrlc_st_time_ind();
        vsi_t_start(GRLC_handle,T3190,T3190_VALUE);
      }
      if(grlc_data->rd.ch_req_in_ack_prog AND mac_dl_ready_ind->last_poll_resp)
      {
        grlc_data->rd.channel_req = TRUE;
      }
      else if(grlc_data->rd.ch_req_in_ack_prog)
      {
        sig_rd_tm_ul_req_cnf();
        grlc_data->rd.ch_req_in_ack_prog = FALSE;
      }

      
      if(grlc_data->rd.release_tbf)  
      {
        /*
         * Packet downlink ack/nack with final ack bit set to 1 was programmed
         */
        grlc_data->rd.release_tbf = FALSE;
        if(mac_dl_ready_ind->last_poll_resp EQ 0)
        {
          /*
           * Transmission from L1 confirmed
           */
          grlc_data->rd.cnt_sent_f_ack++;
        }

      }

       
      if( grlc_data->rd.f_ack_ind AND
          (grlc_data->rd.fn_p_tbf_rel NEQ 0xFFFFFFFF)  AND
          grlc_check_if_tbf_start_is_elapsed ( grlc_data->rd.fn_p_tbf_rel, mac_dl_ready_ind->fn))
      {
        /*
         * no addtional final downlink ack with fbi requested by the network
         * T3192 can be started
         */

        vsi_t_stop(GRLC_handle,T3190);

        if(grlc_data->rd.cnt_sent_f_ack)
        { 
          /*
           * it is only a retransmission of the final dl ack. 
           * Therefore t3192 is restarted, no offset is needed
           */
          vsi_t_start(GRLC_handle,T3192, T3192_Values[grlc_data->downlink_tbf.t3192_val]); 
          TRACE_EVENT_P3("T3192 started:  rel_fn=%ld fn=%ld T3192=%d",grlc_data->rd.fn_p_tbf_rel,mac_dl_ready_ind->fn,grlc_data->downlink_tbf.t3192_val);
        }
        else
        {
          /*
           * the first final dl ack is not retransmitted, therefore retransmission is required.
           * start t3190 and wait for new poll position
           */ 
          vsi_t_start(GRLC_handle,T3190,T3190_VALUE);
          TRACE_EVENT_P1("NO DL REL: ret needed T3190 is running %d",grlc_data->rd.cnt_sent_f_ack);
        }  
        grlc_data->rd.fn_p_tbf_rel = 0xFFFFFFFF;
      }


      if( (delta_fn EQ 4) 
           OR 
          (delta_fn EQ 5))                   
      {
        UBYTE  i=0;
        UBYTE  index;
        while(grlc_data->next_poll_array[grlc_data->poll_start_tbf].cnt 
              AND 
              (i<8))
        {
          if(grlc_data->next_poll_array[grlc_data->poll_start_tbf].poll_type[i] EQ CGRLC_POLL_DATA)
          {
           /* 
            * poll positon in next frame present, 
            * counter for for poll postions in one frame 
            */
            if( grlc_data->rd.channel_req                                OR /* uplink request */
                grlc_data->rd.next_poll_block EQ NEXT_POLL_BLOCK_DL_DATA OR /* last poll block was not ack nack */
                grlc_data->rd.f_ack_ind                                  OR /* final ack indicator */
                !tm_is_ctrl_blk_rdy
                   ( grlc_data->rd.ack_ctrl.cnt_meas_rpt, 
                     grlc_data->rd.ack_ctrl.cnt_other     ) )               /* no ctrl message */
            { 
              if ( !((grlc_data->rd.rlc_mode EQ CGRLC_RLC_MODE_UACK) AND grlc_data->rd.f_ack_ind) )
              {
                /* 
                 * sent downlink ack/nack 
                 */
                 ptr_block = rd_set_acknack();
                 sig_rd_meas_qual_rpt_sent( );
                 grlc_data->tbf_ctrl[grlc_data->dl_index].ack_cnt++;

                 if( grlc_data->rd.ack_ctrl.cnt_meas_rpt > 0 )
                   grlc_data->rd.ack_ctrl.cnt_meas_rpt--;
                 
                 if( grlc_data->rd.ack_ctrl.cnt_other > 0 )
                   grlc_data->rd.ack_ctrl.cnt_other--;

                 grlc_send_normal_burst(ptr_block, NULL, i);
              }
              else
              {
                /*
                 * unacknowledged mode, send packet control acknowledgment
                 */
                if(grlc_data->burst_type EQ CGRLC_BURST_TYPE_NB)
                {
                   ptr_block = grlc_set_packet_ctrl_ack();
                   grlc_send_normal_burst(ptr_block, NULL, i);
                }
                else
                {
                   /* send access burst. */
                   grlc_send_access_burst(i);
                }
              }
              grlc_data->rd.next_poll_block = NEXT_POLL_BLOCK_CTRL;
              if(grlc_data->rd.f_ack_ind)
              {
                grlc_data->rd.release_tbf = TRUE;
              }
            }
            else
            { 
              /* 
               * sent control block 
               */
              ptr_block = tm_get_ctrl_blk( &index, TRUE );
              grlc_send_normal_burst(NULL, ptr_block, i);
              grlc_data->rd.next_poll_block = NEXT_POLL_BLOCK_DL_DATA;
              
              if( grlc_data->rd.ack_ctrl.cnt_meas_rpt EQ 0 )
                grlc_data->rd.ack_ctrl.cnt_meas_rpt = ACK_CNT_NORMAL;
                 
              if( grlc_data->rd.ack_ctrl.cnt_other EQ 0 )
                grlc_data->rd.ack_ctrl.cnt_other = ACK_CNT_NORMAL;
            }
          }
          i++;
        }
      }
      grlc_send_rem_poll_pos(mac_dl_ready_ind->fn);
      break;
    case RD_NET_REL:       
      /* 
       * send packet control acknowledgement and abort TBF
       */
      grlc_send_rem_poll_pos(mac_dl_ready_ind->fn);
      if(grlc_data->rd.release_tbf)
      {
        SET_STATE(RD,RD_NULL);
        grlc_data->rd.release_tbf = FALSE;                
        TRACE_EVENT_P2("poll sent after packet tbf release(DL): current_fn=%ld rel_fn=%ld",
                                        mac_dl_ready_ind->fn,
                                        grlc_data->rd.fn_p_tbf_rel);
        sig_rd_tm_end_of_tbf(FALSE);
      }
      if(grlc_check_if_tbf_start_is_elapsed ( grlc_data->rd.fn_p_tbf_rel, mac_dl_ready_ind->fn))
      {
        grlc_data->rd.release_tbf = TRUE;
        TRACE_EVENT_P2("wait for poll confirm after packet tbf release(DL) current_fn=%ld rel_fn=%ld",
                                        mac_dl_ready_ind->fn,
                                        grlc_data->rd.fn_p_tbf_rel);
      }
	  break;
    default:
      TRACE_ERROR( "SIG_GFF_RD_MAC_READY_IND unexpected" );
      break;
  }
} /* sig_gff_rd_mac_ready_ind() */




/*
+------------------------------------------------------------------------------
| Function    : sig_gff_rd_data
+------------------------------------------------------------------------------
| Description : Handles the SIGNAL sig_gff_rd_data
|
| Parameters  : fn - current frame number
|               tn - current timeslot number
|               block_status - current block_status
|               sp           - sp bit of mac header.
|               bsn          - bsn number
|               fbi          - fbi bit
|               e_bit        - e bit
|               ptr_dl_block - pointer to the dl data block
+------------------------------------------------------------------------------
*/
GLOBAL void sig_gff_rd_data (ULONG fn, UBYTE tn, USHORT block_status,UBYTE rrbp,UBYTE sp,UBYTE bsn,UBYTE fbi,UBYTE e_bit, UBYTE * ptr_dl_block) 
{
  UBYTE bsn_l;
  UBYTE diff;
  BOOL  InRange;
  BOOL  li_correct;
  T_CODING_SCHEME old_cs_type;


  TRACE_FUNCTION( "sig_gff_rd_data" );

  
  grlc_data->tbf_ctrl[grlc_data->dl_index].cnt_ts++;


  /*
   * TESTMODE B
   */
  if (grlc_data->testmode.mode EQ 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.
    */
    if (grlc_data->downlink_tbf.nts > 1)
    {
      if (tn - grlc_data->testmode.dl_ts_offset)
      {
        grlc_data->testmode.rec_data[1].block_status = block_status;
        grlc_data->testmode.rec_data[1].e_bit        = e_bit;
        memcpy(grlc_data->testmode.rec_data[1].payload,ptr_dl_block,50);
      }
      else
      {
        grlc_data->testmode.rec_data[0].block_status = block_status;
        grlc_data->testmode.rec_data[0].e_bit        = e_bit;
        memcpy(grlc_data->testmode.rec_data[0].payload,ptr_dl_block,50);
      }
    }
    else
    {
      grlc_data->testmode.rec_data[0].block_status = block_status;
      grlc_data->testmode.rec_data[0].e_bit        = e_bit;
      memcpy(grlc_data->testmode.rec_data[0].payload,ptr_dl_block,50);
    }

    grlc_data->rd.f_ack_ind = rd_check_fbi(fbi,sp,fn,rrbp);

    vsi_t_stop(GRLC_handle,T3190);
    vsi_t_start(GRLC_handle,T3190,T3190_VALUE);

    TRACE_EVENT_P6("DL DATA in testmode B at fn=%ld: bsn =%d, bs=%d,e_bit=%d, fbi=%d sp=%d "
                            ,fn
                            ,bsn
                            ,block_status
                            ,grlc_data->testmode.rec_data[0].e_bit
                            ,fbi
                            ,sp);
    return;
  }




/******************************************************************************************/
#if defined (_SIMULATION_)
  TRACE_EVENT_P1("DL DATA bsn= %d",bsn);
#endif /* defined (_SIMULATION_) */


  if(grlc_data->rd.vq NEQ grlc_data->rd.vr)
  {
    TRACE_EVENT_P5("wait for neg acked blocks :bsn=%d  vr=%d vq=%d  fbi=%d fn=%ld",
                                                            bsn,
                                                            grlc_data->rd.vr,
                                                            grlc_data->rd.vq,
                                                            fbi,
                                                            fn);
  }
/******************************************************************************************/
  old_cs_type = grlc_data->rd.cs_type;
  switch( GET_STATE( RD ) )
  {    
    case RD_WAIT_FOR_STARTING_TIME_ACK:
      SET_STATE(RD,RD_ACK);    

      /*lint -fallthrough*/

    case RD_ACK     :
      vsi_t_stop(GRLC_handle,T3190); 
      vsi_t_start(GRLC_handle,T3190,T3190_VALUE);
      bsn_l   = bsn;
      InRange = rd_check_window_size(bsn_l);


      if( !InRange  OR (grlc_data->rd.vn[bsn_l & WIN_MOD] EQ VN_RECEIVED))
      {
        grlc_data->tbf_ctrl[grlc_data->dl_index].ret_bsn++;
        
        if( fbi                      AND 
            !grlc_data->rd.f_ack_ind AND
           ((bsn_l+1) & 0x7F) EQ grlc_data->rd.vr)
        { /* WORKAROUND FOR NORTEL, fbi bit is modified , only possible for last bsn*/ 
          
          grlc_data->rd.f_ack_ind=rd_check_fbi(fbi,sp,fn,rrbp);

          TRACE_EVENT_P8("fbi modification in ret bsn=%d,vr=%d,vq=%d,sp=%d,fbi=%d,e=%d ptr[0]=%x,ptr[1]=%x",
                                                                    bsn_l,
                                                                    grlc_data->rd.vr,
                                                                    grlc_data->rd.vq,
                                                                    sp,
                                                                    fbi,
                                                                    e_bit,
                                                                    ptr_dl_block[0],
                                                                    ptr_dl_block[1]);

        }
        
        return;
      }
      grlc_data->rd.rlc_data_len = rd_calc_rlc_data_len(block_status);
      li_correct                = rd_read_li_m_of_block(ptr_dl_block,
                                                        e_bit);
      if((old_cs_type NEQ grlc_data->rd.cs_type) AND
         (old_cs_type NEQ CS_ZERO))
      {
        TRACE_EVENT_P3("DL CS TYPE CHANGED from %d to %d, bsn=%d",old_cs_type,grlc_data->rd.cs_type,bsn_l);
      }
      
      if(!li_correct)
      {
        grlc_data->rd.vn[bsn_l & WIN_MOD] = VN_INVALID;
        TRACE_ERROR( "LI field is longer than RLC data block" );
        return;
      }
     
      /* 
       * compute receive parameter
       */
      rd_comp_rec_par(bsn_l); 

      rd_save_block( bsn_l, &(ptr_dl_block[grlc_data->rd.li_cnt]),
                              fbi );

#if defined (_SIMULATION_)
  TRACE_EVENT_P1("first_ptr= %ld",grlc_data->rd.data_array[bsn_l & WIN_MOD].first);
#endif /* defined (_SIMULATION_) */

      diff = (grlc_data->rd.vq - bsn_l) & 0x7F;
      while( ( diff <= WIN_SIZE)          AND
             (grlc_data->rd.vq NEQ bsn_l) AND 
             (grlc_data->rd.vn[bsn_l & WIN_MOD] EQ VN_RECEIVED )  )
      {
        rd_send_grlc_data_ind(bsn_l);		
        bsn_l = (bsn_l+1)  & 0x7F;
        diff  = (grlc_data->rd.vq - bsn_l) & 0x7F;
      }       
      
      grlc_data->rd.f_ack_ind = rd_check_fbi(fbi,sp,fn,rrbp);
      
      break;
    case RD_REL_ACK :    
      /*
       * all blocks are received, after sending p. dl ack nack timer t3192 will be restarted
       */
      grlc_data->tbf_ctrl[grlc_data->dl_index].ret_bsn++;

      if(grlc_data->rd.f_ack_ind AND sp)
      { /* 
         * will be restarted at sending the dl ack nack: secure, in case of gaps.
         * The value could be zero, therfore a offset is needed
         */ 
        vsi_t_stop(GRLC_handle,T3192); 
        grlc_data->rd.fn_p_tbf_rel = grlc_decode_tbf_start_rel(fn,(USHORT)(rrbp+3));
        TRACE_EVENT_P3("FINAL DL ACK RETR REQIURED t3192=%d rel_fn=%ld c_fn=%ld ",grlc_data->downlink_tbf.t3192_val,grlc_data->rd.fn_p_tbf_rel,fn);
      }
 /*     TRACE_EVENT_P7("BLOCK DISCARDED WAIT FOR TBF RELEASE f_ack=%d bsn=%d sp=%d rrbp=%d vr=%d vq=%d t3192=%d"
                                                               ,grlc_data->rd.f_ack_ind
                                                               ,bsn
                                                               ,sp
                                                               ,rrbp
                                                               ,grlc_data->rd.vr
                                                               ,grlc_data->rd.vq
                                                               ,grlc_data->downlink_tbf.t3192_val);
  */
      break;    
    case RD_WAIT_FOR_STARTING_TIME_UACK:
      SET_STATE(RD,RD_UACK);

      /*lint -fallthrough*/

    case RD_UACK:
      /*
       * After receiving each block the timer T3190 should be restarted. 
       */
      vsi_t_stop(GRLC_handle,T3190); 
      vsi_t_start(GRLC_handle,T3190,T3190_VALUE);

      if(!rd_check_window_size(bsn))
      {
        TRACE_EVENT_P3("UACK bsn outside window: bsn=%d vr=%d vq=%d",bsn,grlc_data->rd.vr,grlc_data->rd.vq);
        return;
      }
      
      grlc_data->rd.rlc_data_len  = rd_calc_rlc_data_len(block_status);
      li_correct                 = rd_read_li_m_of_block(ptr_dl_block, e_bit);
      if(!li_correct)
      {
        TRACE_ERROR( "LI field is longer than RLC data block" );
        return;
      }
     
      /* 
       * compute receive parameter 
       */
      rd_comp_rec_par(bsn); 
      rd_save_block( bsn, &(ptr_dl_block[grlc_data->rd.li_cnt]), fbi );
      if(!grlc_data->rd.inSequence)
        rd_fill_blocks(bsn);

      rd_send_grlc_data_ind(bsn);
      
      grlc_data->rd.f_ack_ind = rd_check_fbi(fbi,sp,fn,rrbp);

      break;
    case RD_REL_UACK:

      if(fbi AND sp)
      {
        vsi_t_stop(GRLC_handle,T3192); 
        grlc_data->rd.fn_p_tbf_rel = grlc_decode_tbf_start_rel(fn,(USHORT)(rrbp+3));
        TRACE_EVENT_P4("data in REL_UACK restart t3192:  bsn=%d   t3192=%d fn=%ld rel_fn=%ld"
                                                               ,bsn
                                                               ,grlc_data->downlink_tbf.t3192_val
                                                               ,fn
                                                               ,grlc_data->rd.fn_p_tbf_rel);

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

} /* sig_gff_rd_data() */

/*
+------------------------------------------------------------------------------
| Function    : sig_ru_rd_get_downlink_release_state
+------------------------------------------------------------------------------
| Description : This function returns true if RD is in release state( if
|               fbi=1 is received in dowonlink data block. 
|
| Parameters  : release_state 
+------------------------------------------------------------------------------
*/
GLOBAL void sig_ru_rd_get_downlink_release_state( BOOL *release_started)
{
  *release_started = FALSE ;

  if( (GET_STATE( RD ) EQ RD_REL_ACK ) OR
      (GET_STATE( RD ) EQ RD_REL_UACK ) )
  {
     *release_started = TRUE;
  }
  else if(GET_STATE( RD ) EQ RD_NULL )
  {
     TRACE_ERROR("ERROR:sig_ru_rd_get_downlink_release_state called in RD NULL state");
     *release_started = TRUE;
  }
}