diff src/g23m-gprs/grlc/grlc_rdf.c @ 1:d393cd9bb723

src/g23m-*: initial import from Magnetite
author Mychaela Falconia <falcon@freecalypso.org>
date Sun, 15 Jul 2018 04:40:46 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/g23m-gprs/grlc/grlc_rdf.c	Sun Jul 15 04:40:46 2018 +0000
@@ -0,0 +1,1307 @@
+/* 
++----------------------------------------------------------------------------- 
+|  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 RD of
+|             entity GRLC.
++----------------------------------------------------------------------------- 
+*/ 
+
+#ifndef GRLC_RDF_C
+#define GRLC_RDF_C
+#endif
+
+#define ENTITY_GRLC
+
+/*==== INCLUDES =============================================================*/
+
+#include "typedefs.h"    /* to get Condat data types */
+#include "vsi.h"        /* to get a lot of macros */
+#include "macdef.h"
+#include "gprs.h"
+#include "gsm.h"        /* to get a lot of macros */
+#include "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_tms.h" 
+#include <string.h>      /* memcpy */
+#include "grlc_f.h" 
+#include "grlc_rdf.h" 
+#include "grlc_meass.h"
+
+/*==== CONST ================================================================*/
+
+/*==== LOCAL VARS ===========================================================*/
+
+/*==== PRIVATE FUNCTIONS ====================================================*/
+
+/*==== PUBLIC FUNCTIONS =====================================================*/
+
+
+/*
++------------------------------------------------------------------------------
+| Function    : rd_init
++------------------------------------------------------------------------------
+| Description : The function rd_init() .... 
+|
+| Parameters  : dummy - description of parameter dummy
+|
++------------------------------------------------------------------------------
+*/
+GLOBAL void rd_init ( void )
+{ 
+  TRACE_FUNCTION( "rd_init" );
+
+  /*
+   * UBYTE
+   */
+  grlc_data->rd.vq              = 0xFF;
+  grlc_data->rd.vr              = 0xFF;
+  grlc_data->rd.li_cnt          = 0xFF;
+  grlc_data->rd.rlc_data_len    = 0xFF;
+  grlc_data->rd.f_ack_ind       = 0xFF;
+  grlc_data->rd.ssn             = 0xFF;      
+        
+  /*
+   * USHORT
+   */
+  grlc_data->rd.pdu_len         = 0xFFFF;
+  /*
+   * BOOL
+   */
+  grlc_data->rd.pdu_complete        = TRUE;
+  grlc_data->rd.channel_req         = FALSE;
+  grlc_data->rd.ch_req_in_ack_prog  = FALSE;  
+  grlc_data->rd.inSequence          = TRUE;
+  /*
+   * struct
+   */
+  grlc_data->rd.ptr_grlc               = NULL;
+  grlc_data->rd.next_poll_block        = NEXT_POLL_BLOCK_NONE;
+  
+  INIT_STATE(RD,RD_NULL);
+
+} /* rd_init() */
+
+
+
+
+
+/*
++------------------------------------------------------------------------------
+| Function    : rd_tbf_init
++------------------------------------------------------------------------------
+| Description : The function rd_tbf_init() .... 
+|
+| Parameters  : dummy - description of parameter dummy
+|
++------------------------------------------------------------------------------
+*/
+GLOBAL void rd_tbf_init ( void )
+{ 
+  TRACE_FUNCTION( "rd_tbf_init" );
+
+  /* 
+   * UBYTE
+   */
+  grlc_data->rd.vq              = 0;
+  grlc_data->rd.vr              = 0;
+  grlc_data->rd.li_cnt          = 0;
+  grlc_data->rd.ssn             = 0;      
+  grlc_data->rd.last_bsn        = 0xFF;
+  grlc_data->rd.bsn_pdu_start   = 0xFF; /*set to zero at receiving the first data block*/
+  grlc_data->rd.cnt_sent_f_ack  = 0;
+  grlc_data->rd.f_ack_ind       = 0;
+  grlc_data->dl_tn_mask         = grlc_data->downlink_tbf.ts_mask;
+
+  /*
+   * USHORT
+   */
+  grlc_data->rd.pdu_len         = 0;
+
+  /*
+   * BOOL
+   */
+  grlc_data->rd.release_tbf        = FALSE;
+  grlc_data->rd.pdu_complete       = TRUE;
+  grlc_data->rd.ch_req_in_ack_prog = FALSE;  
+  grlc_data->rd.v_next_tbf_params  = FALSE;
+  grlc_data->rd.ignore_pdu         = FALSE;
+
+
+  grlc_data->rd.fn_p_tbf_rel       = 0xFFFFFFFF;
+  /*
+   * struct
+   */
+  grlc_data->rd.rlc_mode               = grlc_data->downlink_tbf.rlc_mode;
+  grlc_data->rd.cs_type                = CS_ZERO;
+  /*
+   * arrays
+   */
+  
+  memset(grlc_data->rd.data_array, 0         , WIN_SIZE      * sizeof(grlc_data->rd.data_array[0]) );
+  memset(grlc_data->rd.vn        , VN_INVALID, WIN_SIZE      * sizeof(grlc_data->rd.vn[0])         );
+  memset(grlc_data->rd.li        , 0         , RD_LI_CNT_MAX * sizeof(grlc_data->rd.li[0])         );
+  memset(grlc_data->rd.m         , 0         , RD_LI_CNT_MAX );
+
+} /* rd_tbf_init() */
+
+
+
+/*
++------------------------------------------------------------------------------
+| Function    : rd_read_li_m_of_block
++------------------------------------------------------------------------------
+| Description : The function rd_read_li_m_of_block() .... 
+|
+| Parameters  : dummy - description of parameter dummy
+|
++------------------------------------------------------------------------------
+*/
+GLOBAL BOOL rd_read_li_m_of_block ( UBYTE * ptr_data_i, UBYTE e_bit_i )
+{
+  USHORT  len_sum;
+  BOOL    result;
+
+  TRACE_FUNCTION( "rd_read_li_m_of_block" );
+  
+  len_sum = 0;
+  grlc_data->rd.li_cnt = 0;
+
+  while(!(e_bit_i))
+  {
+    grlc_data->rd.li[grlc_data->rd.li_cnt] = 
+               (ptr_data_i[grlc_data->rd.li_cnt] & 0xFC) >> 2;
+    grlc_data->rd.m [grlc_data->rd.li_cnt] = 
+               (ptr_data_i[grlc_data->rd.li_cnt] & 0x02) >> 1;
+    e_bit_i  = (ptr_data_i[grlc_data->rd.li_cnt] & 0x01);
+    len_sum += grlc_data->rd.li[grlc_data->rd.li_cnt];
+    if((e_bit_i EQ 0) 
+	      AND (!grlc_data->rd.m [grlc_data->rd.li_cnt]))
+    {
+	    /*SZML-RD/001*/
+	    return FALSE;
+    }
+    else if(!(grlc_data->rd.li[grlc_data->rd.li_cnt])
+	    AND (grlc_data->rd.m [grlc_data->rd.li_cnt]))
+    {
+	    TRACE_ERROR("dl block with li=0 and m=1: NOT ALLOWED");
+	    return FALSE;
+    }
+    grlc_data->rd.li_cnt++;
+  }
+
+  if(grlc_data->rd.li_cnt > RD_LI_CNT_MAX)
+  {
+    TRACE_EVENT_P2("li_cnt=%d RD_LI_CNT_MAX=%d",grlc_data->rd.li_cnt,RD_LI_CNT_MAX);
+    TRACE_ERROR("rd li_cnt bigger than RD_LI_CNT_MAX (=8)");
+    TRACE_ASSERT( grlc_data->rd.li_cnt > RD_LI_CNT_MAX );
+    return FALSE;
+  }
+
+  /* 
+   * check if sum of LIs is longer than a rlc data block len 
+   */
+  len_sum += grlc_data->rd.li_cnt;
+  if( (len_sum > grlc_data->rd.rlc_data_len) 
+     OR 
+      ((len_sum EQ grlc_data->rd.rlc_data_len) AND (grlc_data->rd.m [grlc_data->rd.li_cnt-1])) )
+    result = FALSE;
+  else
+    result = TRUE;
+
+  return result;
+
+} /* rd_read_li_m_of_block() */
+
+
+
+/*
++------------------------------------------------------------------------------
+| Function    : rd_out_grlc_data_ind
++------------------------------------------------------------------------------
+| Description : The function rd_out_grlc_data_ind() .... 
+|
+| Parameters  : dummy - description of parameter dummy
+|
++------------------------------------------------------------------------------
+*/
+GLOBAL void rd_out_grlc_data_ind( void )
+{ 
+  T_GRLC_DATA_IND  *prim_ptr;
+  TRACE_FUNCTION( "rd_out_grlc_data_ind" );
+
+  if(grlc_data->rd.ignore_pdu OR
+     grlc_test_mode_active())
+  {
+    /*
+     * this pdu is not passed to LLC because MAX_LLC_PDU_SIZE was exceed or testmode is active
+     */
+    grlc_data->rd.ignore_pdu = FALSE;
+
+    TRACE_EVENT_P4("TARGET PDU END reached at will be ignored: len=%ld, bsn=pdu_start=%d vr = %d testmode=%d "
+                                                                                ,grlc_data->rd.pdu_len
+                                                                                ,grlc_data->rd.bsn_pdu_start
+                                                                                ,grlc_data->rd.vr
+                                                                                ,grlc_test_mode_active());
+
+    rd_free_desc_list_partions();
+    return;
+  }
+
+  if(grlc_data->rd.rlc_mode EQ CGRLC_RLC_MODE_ACK)
+  {
+    PALLOC_DESC (grlc_data_ind, GRLC_DATA_IND);//lint !e413
+    prim_ptr  = grlc_data_ind;
+  }
+  else
+  {
+    PALLOC_DESC (grlc_unitdata_ind, GRLC_UNITDATA_IND);//lint !e413
+    prim_ptr  = (T_GRLC_DATA_IND*)grlc_unitdata_ind;
+  }
+
+  memcpy(  prim_ptr,
+           &grlc_data->rd.grlc_data_ind,
+           sizeof(T_GRLC_DATA_IND) );
+  PSEND(hCommLLC, prim_ptr);
+
+  grlc_data->rd.grlc_data_ind.desc_list.first           = NULL;
+  grlc_data->rd.pdu_complete                            = TRUE;
+  grlc_data->tbf_ctrl[grlc_data->dl_index].rlc_oct_cnt += grlc_data->rd.pdu_len;
+  grlc_data->tbf_ctrl[grlc_data->dl_index].pdu_cnt++;
+
+} /* rd_out_grlc_data_ind() */
+  
+#ifdef _SIMULATION_
+
+/*
++------------------------------------------------------------------------------
+| Function    : rd_out_grlc_data_ind_test
++------------------------------------------------------------------------------
+| Description : The function rd_out_grlc_data_ind_test() .... 
+|
+| Parameters  : dummy - description of parameter dummy
+|
++------------------------------------------------------------------------------
+*/
+GLOBAL void rd_out_grlc_data_ind_test( void )
+{ 
+  T_NEXT_ARRAY          *ptr_next_array = NULL;
+  T_NEXT_ARRAY          *ptr_help = NULL;
+  UBYTE                 *ptr_pos  = NULL,cnt=0;
+  USHORT                sdu_len_in_bits;
+  T_GRLC_DATA_IND_TEST   *prim_ptr = NULL;
+
+  TRACE_FUNCTION( "rd_out_grlc_data_ind_test" );
+
+   
+   if(grlc_data->rd.ignore_pdu OR 
+     grlc_test_mode_active())
+  {
+    /*
+     * this pdu is not passed to LLC because MAX_LLC_PDU_SIZE was exceed or testmode is active
+     */
+    grlc_data->rd.ignore_pdu = FALSE;
+
+    TRACE_EVENT_P4("SIMULATION PDU END reached at will be ignored: len=%ld, bsn=pdu_start=%d vr = %d testmode=%d"
+                                                                                ,grlc_data->rd.pdu_len
+                                                                                ,grlc_data->rd.bsn_pdu_start
+                                                                                ,grlc_data->rd.vr
+                                                                                ,grlc_test_mode_active());
+    rd_free_desc_list_partions();
+    return;
+  }
+  sdu_len_in_bits = grlc_data->rd.pdu_len * 8;
+
+  if(grlc_data->rd.rlc_mode EQ CGRLC_RLC_MODE_ACK)
+  {
+    PALLOC_SDU (grlc_data_ind_test, GRLC_DATA_IND_TEST, sdu_len_in_bits);
+    prim_ptr  = grlc_data_ind_test;
+  }
+  else
+  {
+    PALLOC_SDU (grlc_unitdata_ind_test, GRLC_UNITDATA_IND_TEST, sdu_len_in_bits);
+    prim_ptr  = (T_GRLC_DATA_IND_TEST*)grlc_unitdata_ind_test;
+  }
+  {
+    /* 
+     * copy from description list to test primitive
+     */  
+    prim_ptr->tlli      = grlc_data->downlink_tbf.tlli;
+    prim_ptr->sdu.l_buf = grlc_data->rd.pdu_len * 8;
+    prim_ptr->sdu.o_buf = 0;
+    ptr_next_array      = 
+      (T_NEXT_ARRAY*)grlc_data->rd.grlc_data_ind.desc_list.first;
+    ptr_pos = prim_ptr->sdu.buf;
+    cnt=0;
+    do
+    {
+      cnt++;
+      memcpy(ptr_pos,
+            ptr_next_array->data,
+            ptr_next_array->len );
+      ptr_pos = &ptr_pos[ptr_next_array->len];
+      ptr_next_array = (T_NEXT_ARRAY*)ptr_next_array->next;
+    }
+    while(ptr_next_array NEQ NULL);
+    PSEND(hCommLLC, prim_ptr);
+    TRACE_EVENT_P1("SEND PARTIONS =%d",cnt);
+
+  }
+
+  grlc_data->tbf_ctrl[grlc_data->dl_index].rlc_oct_cnt += grlc_data->rd.pdu_len;
+  grlc_data->tbf_ctrl[grlc_data->dl_index].pdu_cnt++;
+
+
+  /* delete blocks which are sent to LLC: only possible in test environment
+   * usally, deleting of the description list is task of LLC
+   */
+   rd_free_desc_list_partions();
+
+} /* rd_out_grlc_data_ind_test() */  
+
+#endif /* _SIMULATION_ */
+
+
+
+
+/*
++------------------------------------------------------------------------------
+| Function    : rd_send_grlc_data_ind
++------------------------------------------------------------------------------
+| Description : The function rd_send_grlc_data_ind() .... 
+|
+| Parameters  : dummy - description of parameter dummy
+|
++------------------------------------------------------------------------------
+*/
+GLOBAL void rd_send_grlc_data_ind ( UBYTE bsn_i )
+{ 
+  T_NEXT_ARRAY  *ptr_block;
+  UBYTE         pdu_cnt;
+  UBYTE         compl_pdu;
+  TRACE_FUNCTION( "rd_send_grlc_data_ind" );
+  
+  
+  pdu_cnt   = grlc_data->rd.data_array[bsn_i & WIN_MOD].pdu_cnt;
+
+  if(grlc_data->rd.data_array[bsn_i & WIN_MOD].pdu_complete)
+    compl_pdu   = grlc_data->rd.data_array[bsn_i & WIN_MOD].pdu_cnt;
+  else if (pdu_cnt)
+    compl_pdu   = grlc_data->rd.data_array[bsn_i & WIN_MOD].pdu_cnt -1;
+  else
+    compl_pdu   = 0;
+
+  if ((ptr_block = grlc_data->rd.data_array[bsn_i & WIN_MOD].first) EQ NULL)
+  {
+    TRACE_EVENT_P4("NO DATA IND first empty bsn=%d bsn_mod=%d vr=%d vq=%d", bsn_i, bsn_i&WIN_MOD,grlc_data->rd.vr,grlc_data->rd.vq);
+    return;
+  }
+  
+  do
+  {
+    if(grlc_data->rd.pdu_complete)
+    {
+      grlc_data->rd.ptr_grlc                       = ptr_block;
+      grlc_data->rd.grlc_data_ind.desc_list.first  = (ULONG) grlc_data->rd.ptr_grlc;
+      grlc_data->rd.pdu_len                        = ptr_block->len;
+      grlc_data->rd.bsn_pdu_start                  = bsn_i;
+    }
+    else
+    {
+      grlc_data->rd.pdu_len        += ptr_block->len;
+      grlc_data->rd.ptr_grlc->next  = (ULONG*) ptr_block;
+      grlc_data->rd.ptr_grlc        = (T_NEXT_ARRAY*) grlc_data->rd.ptr_grlc->next;
+    }
+    grlc_data->rd.pdu_complete    = FALSE;
+ 
+    if(grlc_data->rd.pdu_len > MAX_LLC_PDU_SIZE)
+    {
+      TRACE_EVENT_P5("PDU SIZE TO BIG = %ld  pdu_st=%d vr=%d compl_pdu=%d pdu_cnt=%d"
+                                                              ,grlc_data->rd.pdu_len
+                                                              ,grlc_data->rd.bsn_pdu_start
+                                                              ,grlc_data->rd.vr
+                                                              ,compl_pdu
+                                                              ,pdu_cnt);
+      if(!compl_pdu)
+      {
+        /* no pdu in data block */
+        grlc_data->rd.ignore_pdu    = TRUE;
+        
+        /* free only linked partions */
+        rd_free_desc_list_partions();
+
+        return;
+      }
+      else
+      {
+        /* pdu boundary in data block */
+        ptr_block = (T_NEXT_ARRAY*) ptr_block->next;
+        grlc_data->rd.ptr_grlc->next = NULL;
+
+        /*
+         * free only linked partions
+         */
+        rd_free_desc_list_partions();
+        
+        compl_pdu--;       
+      }
+
+    }
+    else if( compl_pdu )
+    {
+
+      TRACE_EVENT_P5("GRLC_DATA_IND len=%ld bsn_start=%d bsn_end=%d las_len=%d dl_fn=%ld"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+                                                                              ,grlc_data->rd.pdu_len
+                                                                              ,grlc_data->rd.bsn_pdu_start
+                                                                              ,bsn_i
+                                                                              ,ptr_block->len
+                                                                              ,grlc_data->dl_fn);    
+      ptr_block                                      = (T_NEXT_ARRAY*) ptr_block->next;
+      grlc_data->rd.ptr_grlc->next                   = NULL;
+      grlc_data->rd.grlc_data_ind.desc_list.list_len = grlc_data->rd.pdu_len;
+      grlc_data->rd.grlc_data_ind.tlli               = grlc_data->downlink_tbf.tlli;
+
+      #ifdef _TARGET_
+      {
+          rd_out_grlc_data_ind();
+      }
+      #     endif   
+          
+#     ifdef _SIMULATION_
+      {
+        rd_out_grlc_data_ind_test();
+      }
+#     endif      
+      grlc_data->rd.pdu_complete = TRUE;
+      compl_pdu--;
+    }
+    if(!pdu_cnt)
+      pdu_cnt = 0;
+    else
+      pdu_cnt--;
+  }
+  while(pdu_cnt);
+
+  grlc_data->rd.pdu_complete = grlc_data->rd.data_array[bsn_i & WIN_MOD].pdu_complete;
+
+  /*
+   * block combined, first element reseted
+   */
+  grlc_data->rd.data_array[bsn_i & WIN_MOD].first = NULL;
+
+} /* rd_send_grlc_data_ind() */
+
+
+
+/*
++------------------------------------------------------------------------------
+| Function    : rd_check_window_size
++------------------------------------------------------------------------------
+| Description : The function rd_check_window_size() .... 
+|
+| Parameters  : dummy - description of parameter dummy
+|
++------------------------------------------------------------------------------
+*/
+GLOBAL BOOL rd_check_window_size ( UBYTE bsn_i )
+{ 
+  BOOL  result;
+
+  TRACE_FUNCTION( "rd_check_window_size" );
+
+  if(grlc_data->rd.rlc_mode EQ CGRLC_RLC_MODE_ACK)
+  {
+    if( bsn_i >= grlc_data->rd.ssn )
+    {
+      if((bsn_i - grlc_data->rd.ssn) < WIN_SIZE)
+      {
+        result = TRUE;
+      }
+      else
+      {
+        result = FALSE;
+      }
+    }
+    else 
+    {
+      if((bsn_i + 128 - grlc_data->rd.ssn) < WIN_SIZE)
+      {
+        result = TRUE;
+      }
+      else
+      {
+        result = FALSE;
+      }
+    }
+  }
+  else if(grlc_data->rd.rlc_mode EQ CGRLC_RLC_MODE_UACK)
+  {
+    if( bsn_i >= grlc_data->rd.vr )
+    {
+      if((bsn_i - grlc_data->rd.vr) < WIN_SIZE)
+      {
+        result = TRUE;
+      }
+      else
+      {
+        result = FALSE;
+      }
+    }
+    else 
+    {
+      if((bsn_i + 128 - grlc_data->rd.vr) < WIN_SIZE)
+      {
+        result = TRUE;
+      }
+      else
+      {
+        result = FALSE;
+      }
+    }
+  }
+  else
+  {
+    result =FALSE;
+    TRACE_EVENT_P4("unknown rlc mode in rd_check_window_size: rlc_mode=%d, bsn_i=%d,ssn=%d,vr=%d "
+                                                                                                  ,grlc_data->rd.rlc_mode
+                                                                                                  ,bsn_i
+                                                                                                  ,grlc_data->rd.ssn
+                                                                                                  ,grlc_data->rd.vr);
+  }
+
+
+  return result;
+} /* rd_check_window_size() */
+
+
+
+/*
++------------------------------------------------------------------------------
+| Function    : rd_save_block
++------------------------------------------------------------------------------
+| Description : The function rd_save_block() .... 
+|
+| Parameters  : bsn_i             - block sequence number of the saved block
+|               *ptr_data_block_i - pointer the data field of the rlc data 
+|                                   block without header and li field
+|               fbi_i             - final block indication bit of the received
+|                                   data block
+|
++------------------------------------------------------------------------------
+*/
+GLOBAL void rd_save_block ( UBYTE bsn_i, UBYTE * ptr_data_block_i, UBYTE fbi_i)
+{ 
+  T_NEXT_ARRAY  *ptr_temp=NULL;
+  UBYTE         block_nr;
+  UBYTE         i;
+  UBYTE         *ptr_data;
+  USHORT        len;  
+  TRACE_FUNCTION( "rd_save_block" );
+
+  if(!(grlc_data->rd.li_cnt))
+  {
+    /*
+     * only a part of a pdu
+     */
+    MALLOC(ptr_temp, sizeof(T_NEXT_ARRAY));
+    ptr_temp->next = NULL;
+    ptr_temp->len  = grlc_data->rd.rlc_data_len;
+    memcpy((ptr_temp->data),(ptr_data_block_i),(ptr_temp->len));
+    grlc_data->rd.data_array[bsn_i & WIN_MOD].first = ptr_temp;
+    if(fbi_i)
+    {
+      grlc_data->rd.data_array[bsn_i & WIN_MOD].pdu_cnt = 1;
+      grlc_data->rd.data_array[bsn_i & WIN_MOD].pdu_complete =TRUE;
+    }
+    else
+    {
+      grlc_data->rd.data_array[bsn_i & WIN_MOD].pdu_cnt = 0;
+      grlc_data->rd.data_array[bsn_i & WIN_MOD].pdu_complete =FALSE;
+    }
+
+  }
+  else
+  {
+    /* 
+     * for the first part 
+     */
+    MALLOC(ptr_temp, sizeof(T_NEXT_ARRAY));
+    if(grlc_data->rd.li[0] EQ 0 )
+      ptr_temp->len  = grlc_data->rd.rlc_data_len-1;
+    else
+      ptr_temp->len  = grlc_data->rd.li[0];
+
+    ptr_data = ptr_data_block_i;
+    memcpy((ptr_temp->data),
+           (ptr_data),
+           (ptr_temp->len));
+    grlc_data->rd.data_array[bsn_i & WIN_MOD].first = ptr_temp;            
+    block_nr = 1;
+    while((block_nr <= grlc_data->rd.li_cnt) AND (grlc_data->rd.m[block_nr-1]))
+    {
+      ptr_data       = &(ptr_data[ptr_temp->len]);
+      MALLOC(ptr_temp->next, sizeof(T_NEXT_ARRAY));
+      ptr_temp       = (T_NEXT_ARRAY *)ptr_temp->next;
+      /*
+       * check if it islast pdu in rlc data block
+       */
+      if((block_nr EQ grlc_data->rd.li_cnt)
+		      OR (grlc_data->rd.li[block_nr] EQ 0))
+      {
+        /*
+         * last block, len is REST
+         */
+        len = 0;
+        for(i=0; i < grlc_data->rd.li_cnt; i++)
+          len += grlc_data->rd.li[i];
+        ptr_temp->len  = grlc_data->rd.rlc_data_len - grlc_data->rd.li_cnt - len;
+      }
+      else
+      {
+        /* 
+         * not last part 
+         */
+        ptr_temp->len  = grlc_data->rd.li[block_nr];
+      }
+      memcpy((ptr_temp->data),
+             (ptr_data),
+             (ptr_temp->len));
+      block_nr++;
+    }    
+    ptr_temp->next = NULL;
+    if(grlc_data->rd.m[grlc_data->rd.li_cnt-1] EQ 0)
+	    grlc_data->rd.data_array[bsn_i & WIN_MOD].pdu_cnt = grlc_data->rd.li_cnt;
+	  else
+	    grlc_data->rd.data_array[bsn_i & WIN_MOD].pdu_cnt = grlc_data->rd.li_cnt + 1;
+
+	  if(fbi_i
+		  OR 
+		  ((grlc_data->rd.li[grlc_data->rd.li_cnt-1])
+		    AND (grlc_data->rd.m[grlc_data->rd.li_cnt-1] EQ 0)))
+	    grlc_data->rd.data_array[bsn_i & WIN_MOD].pdu_complete = TRUE;
+	  else
+	    grlc_data->rd.data_array[bsn_i & WIN_MOD].pdu_complete = FALSE;
+
+  }
+  
+} /* rd_save_block() */
+
+
+
+/*
++------------------------------------------------------------------------------
+| Function    : rd_comp_rec_par
++------------------------------------------------------------------------------
+| Description : The function rd_comp_rec_par() .... 
+|
+| Parameters  : dummy - description of parameter dummy
+|
++------------------------------------------------------------------------------
+*/
+GLOBAL void rd_comp_rec_par ( UBYTE bsn_i )
+{ 
+  UBYTE vq_help;
+  TRACE_FUNCTION( "rd_comp_rec_par" );
+
+  if(grlc_data->rd.rlc_mode EQ CGRLC_RLC_MODE_ACK)
+  {
+    if( ( grlc_data->rd.vr >= grlc_data->rd.ssn)  && 
+        ( (bsn_i >= grlc_data->rd.vr ) || 
+          (bsn_i < grlc_data->rd.ssn) ) )
+      grlc_data->rd.vr = (bsn_i+1) & 0x7F;
+    else if( ( grlc_data->rd.vr < grlc_data->rd.ssn)  && 
+             ( (bsn_i >= grlc_data->rd.vr ) && 
+               (bsn_i < grlc_data->rd.ssn) ) )
+      grlc_data->rd.vr = (bsn_i+1) & 0x7F;
+    
+    grlc_data->rd.vn[bsn_i & WIN_MOD] = VN_RECEIVED;
+    vq_help = grlc_data->rd.vq;
+    while( (grlc_data->rd.vn[vq_help & WIN_MOD]  EQ VN_RECEIVED) AND 
+            vq_help NEQ grlc_data->rd.vr)
+    {
+      vq_help = (vq_help+1) & 0x7F;
+    }
+    grlc_data->rd.vq = vq_help;
+  }
+  else if(grlc_data->rd.rlc_mode EQ CGRLC_RLC_MODE_UACK)
+  {
+    if(grlc_data->rd.vr EQ bsn_i)
+    {
+      grlc_data->rd.vr         = (1+grlc_data->rd.vr) % 128;
+      grlc_data->rd.vq         = (1+grlc_data->rd.vq) % 128;
+      grlc_data->rd.inSequence = TRUE;
+    }
+    else
+    {
+      grlc_data->rd.vr         = bsn_i;
+      grlc_data->rd.inSequence = FALSE;    
+    }
+  }
+  else
+    TRACE_ERROR(" unknown RLC Mode during dl tbf in rd_comp_rec_par");
+
+
+  
+} /* rd_comp_rec_par() */
+
+
+
+
+/*
++------------------------------------------------------------------------------
+| Function    : rd_check_fbi
++------------------------------------------------------------------------------
+| Description : The function rd_check_fbi() .... 
+|
+| Parameters  : dummy - description of parameter dummy
+|
++------------------------------------------------------------------------------
+*/
+GLOBAL UBYTE rd_check_fbi ( UBYTE fbi_i, UBYTE sp , ULONG fn , UBYTE rrbp  )
+{ 
+  TRACE_FUNCTION( "rd_check_fbi" );
+ 
+  /*
+   * mark the bsn with fbi=1
+   */
+  if(fbi_i)
+  {
+    grlc_data->rd.last_bsn = grlc_data->rd.vr;
+    if(!sp)
+    {
+      TRACE_EVENT_P1("NO SP BUT FINAL DATA BLOCK t3192=%d",grlc_data->downlink_tbf.t3192_val);
+    }
+  }
+
+
+  /*
+   * if last bsn is left window element, than tbf is going to be released
+   */
+  if(grlc_data->rd.last_bsn EQ grlc_data->rd.vq AND
+     !grlc_data->rd.f_ack_ind) /* to avoid retransmission */ 
+  {
+    PALLOC(prim,CGRLC_T3192_STARTED_IND);
+    PSEND(hCommGRR,prim);
+    if(grlc_data->rd.rlc_mode EQ CGRLC_RLC_MODE_ACK )
+    {
+      SET_STATE(RD,RD_REL_ACK);
+    }
+    else
+    {
+      SET_STATE(RD,RD_REL_UACK);
+    }
+    
+    grlc_data->tbf_ctrl[grlc_data->dl_index].fbi = 1;
+
+    if(sp)
+    {
+      grlc_data->rd.fn_p_tbf_rel = grlc_decode_tbf_start_rel(fn,(USHORT)(rrbp+3));
+    }
+    return 1;
+  }
+  else
+  {
+    return 0;
+  }
+}/* rd_check_fbi() */
+
+
+
+
+/*
++------------------------------------------------------------------------------
+| Function    : rd_set_acknack
++------------------------------------------------------------------------------
+| Description : The function rd_set_acknack() .... 
+|
+| Parameters  : dummy - description of parameter dummy
+|
++------------------------------------------------------------------------------
+*/
+GLOBAL UBYTE* rd_set_acknack ( void )
+{ 
+  MCAST (u_dl_ack,U_GRLC_DL_ACK);
+  UBYTE * ptr_block;
+  UBYTE i;
+  UBYTE index;
+  UBYTE help,neg_ack=0;
+  ULONG rbb1=0,rbb2=0,dummy1=0,dummy2=0;
+
+  TRACE_FUNCTION( "rd_set_acknack" );
+
+  
+  if(grlc_data->rd.channel_req AND (grlc_data->tbf_type EQ TBF_TYPE_DL))
+  {
+    u_dl_ack->v_chan_req_des = 1;
+    u_dl_ack->chan_req_des   = grlc_data->chan_req_des;    
+    grlc_data->rd.channel_req = 0;
+    grlc_data->rd.ch_req_in_ack_prog = TRUE;
+	 /* TRACE_EVENT_P6("channel req des in dl ack nack:ptp=%d,rp=%d,rlc_mode=%d,llc_pt=%d,rlc_oc=%d, pst=%d",
+                                  grlc_data->chan_req_des.peak_thr_class,
+                                  grlc_data->chan_req_des.radio_prio,
+                                  grlc_data->chan_req_des.rlc_mode,
+                                  grlc_data->chan_req_des.llc_pdu_type,
+                                  grlc_data->chan_req_des.rlc_octet_cnt,
+                                  grlc_data->prim_start_tbf);*/
+    if(grlc_data->prim_start_tbf >= PRIM_QUEUE_SIZE_TOTAL)
+    {
+      TRACE_EVENT_P3("PST=%d PSF=%d PDU=%d: rd_set_acknack"
+                                                           ,grlc_data->prim_start_tbf
+                                                           ,grlc_data->prim_start_free
+                                                           ,grlc_data->grlc_data_req_cnt);
+    }
+
+  }
+  else
+  {
+    u_dl_ack->v_chan_req_des = 0;
+    grlc_data->rd.channel_req = 0; 
+  }
+  
+  u_dl_ack->msg_type               = U_GRLC_DL_ACK_c;
+  u_dl_ack->dl_tfi                 = grlc_data->dl_tfi;
+  u_dl_ack->ack_nack_des.f_ack_ind = grlc_data->rd.f_ack_ind;
+  u_dl_ack->ack_nack_des.ssn       = grlc_data->rd.vr;
+  memset(u_dl_ack->ack_nack_des.rbb, 1, WIN_SIZE );
+  for(i=0; i< WIN_SIZE; i++)
+  {
+    if(grlc_data->rd.vr EQ grlc_data->rd.ssn OR grlc_data->rd.rlc_mode EQ CGRLC_RLC_MODE_UACK)
+      break;
+    index = (grlc_data->rd.vr-1-i) & 0x7F;
+    if(grlc_data->rd.vn[index & WIN_MOD] NEQ VN_RECEIVED)
+    {
+      u_dl_ack->ack_nack_des.rbb[WIN_SIZE-1-i] = 0; 
+      neg_ack++;
+    }
+    if(grlc_data->rd.ssn EQ index )      
+    {
+      help  =  grlc_data->rd.ssn;
+      while(help NEQ grlc_data->rd.vq)
+      {
+        grlc_data->rd.vn[help & WIN_MOD] = VN_INVALID;
+        help = (help + 1) & 0x7F; 
+      }
+      break;
+    }
+  }
+
+  /* TRACE ONLY if there is channel desc or negative ack or the final ack*/
+  if(u_dl_ack->v_chan_req_des OR
+     u_dl_ack->ack_nack_des.f_ack_ind OR
+     neg_ack)
+  {
+    if(neg_ack AND !u_dl_ack->ack_nack_des.f_ack_ind)
+    {
+      for(i=0; i<32;i++)
+      {
+        dummy1 = u_dl_ack->ack_nack_des.rbb[WIN_SIZE-1-i];
+        dummy2 = u_dl_ack->ack_nack_des.rbb[WIN_SIZE-1-i-32];
+        rbb1  += dummy1 <<i;
+        rbb2  += dummy2 <<i;
+      }
+      TRACE_EVENT_P7("dl_ack:p_fn=%ld,CD=%d  ssn=%d nacks=%d vq=%d rbb2=%lx rbb1=%lx",
+                                          grlc_data->next_poll_array[grlc_data->poll_start_tbf].fn,
+                                          u_dl_ack->v_chan_req_des,
+                                          u_dl_ack->ack_nack_des.ssn,
+                                          neg_ack,                                         
+                                          grlc_data->rd.vq,
+                                          rbb2,
+                                          rbb1); 
+    }
+    else
+    {
+      rbb1 = 0xFFFFFFFF;
+      rbb2 = 0xFFFFFFFF;
+      TRACE_EVENT_P6("dl_ack:p_fn=%ld,CD=%d fbi=%d ssn=%d nacks=%d vq=%d",
+                                          grlc_data->next_poll_array[grlc_data->poll_start_tbf].fn,
+                                          u_dl_ack->v_chan_req_des,
+                                          u_dl_ack->ack_nack_des.f_ack_ind,
+                                          u_dl_ack->ack_nack_des.ssn,
+                                          neg_ack,                                         
+                                          grlc_data->rd.vq);
+    }
+  }
+  else
+  {
+    rbb1 = 0xFFFFFFFF;
+    rbb2 = 0xFFFFFFFF;  
+  }
+
+  u_dl_ack->chan_qual_rep.c_value = 0;
+  u_dl_ack->chan_qual_rep.rxqual  = 0;
+  u_dl_ack->chan_qual_rep.signvar = 0;
+
+  {
+    /* processing channel quality report */
+
+    /* processing of C value */
+    u_dl_ack->chan_qual_rep.c_value = meas_grlc_c_get_value( );
+
+    /* processing of RXQUAL value */
+    u_dl_ack->chan_qual_rep.rxqual  = meas_sq_get_rxqual_value( );
+
+    /* processing of signal variance */
+    u_dl_ack->chan_qual_rep.signvar = meas_sv_get_value( );
+
+    /* processing of relative interference levels */
+    meas_int_get_rel_i_level( &u_dl_ack->chan_qual_rep.ilev );
+  } 
+
+#if !defined (NTRACE)
+
+  if( grlc_data->meas.v_im_trace NEQ 0 )
+  {
+    TRACE_EVENT_P3( "rd_set_acknack: %d %d %d",
+                    u_dl_ack->chan_qual_rep.c_value,
+                    u_dl_ack->chan_qual_rep.rxqual,
+                    u_dl_ack->chan_qual_rep.signvar );
+  }
+
+#endif /* #if !defined (NTRACE) */
+
+#ifdef REL99
+
+  u_dl_ack->v_release_99_str_u_grlc_dl_ack = 1;
+  
+  if (grlc_data->pfi_support AND u_dl_ack->v_chan_req_des )
+  {
+    u_dl_ack->release_99_str_u_grlc_dl_ack.v_pfi = 1;
+    u_dl_ack->release_99_str_u_grlc_dl_ack.pfi = grlc_data->pfi_value;  
+  }
+  else
+  {
+    u_dl_ack->release_99_str_u_grlc_dl_ack.v_pfi = 0;
+  }
+
+#endif
+
+
+  grlc_data->rd.ssn   = grlc_data->rd.vq;  
+  ptr_block = (_decodedMsg);
+
+
+  return ptr_block;
+} /* rd_set_acknack() */
+
+
+/*
++------------------------------------------------------------------------------
+| Function    : rd_calc_rlc_data_len
++------------------------------------------------------------------------------
+| Description : The function rd_calc_rlc_data_len() calculates the Data size of
+|               an RLC data block depending on coding scheme. 
+|               The size is described in bytes.
+|               
+| Parameters  : block_status_i - includes the Coding scheme which 
+|               determines the size of an RLC data block
+|                 
++------------------------------------------------------------------------------
+*/
+GLOBAL UBYTE rd_calc_rlc_data_len ( USHORT  block_status_i )
+{ 
+  UBYTE result=0;
+  TRACE_FUNCTION( "rd_calc_rlc_data_len" );
+  
+  /*
+   * the coding scheme is only in the first four bits
+   */
+  block_status_i = block_status_i & 0x000F; 
+
+  switch( block_status_i)
+  {
+  case 2:
+    grlc_data->rd.cs_type  = CS_1;
+    break;
+  case 4:
+    grlc_data->rd.cs_type  = CS_2;
+    break;
+  case 5:
+    grlc_data->rd.cs_type  = CS_3;
+    break;
+  case 6:
+    grlc_data->rd.cs_type  = CS_4;
+    break;
+  default:
+    TRACE_EVENT("No Coding Scheme in RLC data block defined, old CS is used");
+    break;
+  }
+  
+  
+  switch( grlc_data->rd.cs_type)
+  {
+  /*
+   * NO CS defined in all previously received RLC data blocks, 
+   * therefore default CS_1 is used
+   */
+  case CS_ZERO: 
+  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:
+    TRACE_ERROR("unknown Coding Scheme");
+    break;
+  }
+
+  return result;
+} /* rd_calc_rlc_data_len() */
+
+
+
+/*
++------------------------------------------------------------------------------
+| Function    : rd_fill_blocks
++------------------------------------------------------------------------------
+| Description : The function rd_fill_blocks() fills not received but needed 
+|               RLC data blocks with the value Zero.
+|
+| Parameters  : bsn_i - bsn value of the recently received RLC data block, 
+|               which is not inSequence(not equal VR at receiving the block)
+|
++------------------------------------------------------------------------------
+*/
+GLOBAL void rd_fill_blocks ( UBYTE bsn_i )
+{ 
+  T_NEXT_ARRAY * ptr_temp=NULL;
+  TRACE_FUNCTION( "rd_fill_blocks" );
+
+  do
+  {
+    MALLOC(ptr_temp, sizeof(T_NEXT_ARRAY));
+    ptr_temp->next = NULL;
+    ptr_temp->len  = grlc_data->rd.rlc_data_len;
+    memset(ptr_temp->data,0,ptr_temp->len);
+    grlc_data->rd.data_array[grlc_data->rd.vq & WIN_MOD].first = ptr_temp;
+    grlc_data->rd.data_array[grlc_data->rd.vq & WIN_MOD].pdu_cnt = 0;
+    grlc_data->rd.data_array[grlc_data->rd.vq & WIN_MOD].pdu_complete =FALSE;
+    rd_send_grlc_data_ind(grlc_data->rd.vq);
+    grlc_data->rd.vq = (1+grlc_data->rd.vq) % 128; /*modulo 128*/
+  }
+  while(grlc_data->rd.vq NEQ bsn_i);
+
+  grlc_data->rd.vq         = (1+grlc_data->rd.vq) % 128; /*modulo 128*/
+  grlc_data->rd.vr         = grlc_data->rd.vq;
+  grlc_data->rd.inSequence = TRUE;
+
+
+
+} /* rd_fill_blocks() */
+
+
+
+
+/*
++------------------------------------------------------------------------------
+| Function    : rd_calc_delta_fn
++------------------------------------------------------------------------------
+| Description : The function rd_calc_delta_fn() calculates delta_fn. It is 
+|               needed at receiving mac_ready_ind to send a poll block.
+|               In Target Enviroment, the poll block(Ack/nack or Control block)
+|               must be calculated one radio block earlier then the send time.
+|               This is needed due to the functional interface.
+| Parameters  : fn_i - framenumber for the uplink call 
+|               
+|
++------------------------------------------------------------------------------
+*/
+GLOBAL ULONG rd_calc_delta_fn ( ULONG fn_i )
+{ 
+  ULONG result;
+  TRACE_FUNCTION( "rd_calc_delta_fn" );
+
+  #ifdef _TARGET_
+  {
+    if(fn_i EQ FN_MAX-5)
+      result = grlc_data->next_poll_array[grlc_data->poll_start_tbf].fn + 5;
+    else
+      result = grlc_data->next_poll_array[grlc_data->poll_start_tbf].fn - fn_i;
+  }
+#endif
+
+#ifdef _SIMULATION_
+  {
+    if(fn_i EQ grlc_data->next_poll_array[grlc_data->poll_start_tbf].fn)
+      result = 4; /*valid: send block*/
+    else
+      result = 0; /*no poll sending*/
+  }
+#endif
+
+  return result;
+
+
+} /* rd_calc_delta_fn() */
+
+/*
++------------------------------------------------------------------------------
+| Function    : rd_free_desc_list_partions
++------------------------------------------------------------------------------
+| Description : The function rd_free_desc_list_partions() frees the partions,
+|               which are linked for a candidate LLC pdu. PDU is stored as a 
+|               description list, but not passed to LLC because of incomplete.
+| Parameters  :  
+|               
++------------------------------------------------------------------------------
+*/
+GLOBAL void rd_free_desc_list_partions ( void )
+{
+  UBYTE          cnt  = 0;
+  T_NEXT_ARRAY  *help = NULL;
+
+  TRACE_FUNCTION( "rd_free_desc_list_partions" );
+
+  help = (T_NEXT_ARRAY*)grlc_data->rd.grlc_data_ind.desc_list.first;
+
+  grlc_data->rd.grlc_data_ind.desc_list.first = NULL;
+
+  while(help != NULL)
+  {
+    T_NEXT_ARRAY  *ptr = (T_NEXT_ARRAY*) help->next;
+    MFREE(help);
+    help = ptr;
+    cnt++;
+  }
+
+
+  #ifdef _SIMULATION_
+  TRACE_EVENT_P1("freed partion (linked)  : cnt=%d",cnt);
+#endif /* _SIMULATION_*/
+
+
+  grlc_data->rd.pdu_complete  = TRUE;
+  grlc_data->rd.pdu_len       = 0;
+
+
+} /* rd_free_desc_list_partions() */
+
+/*
++------------------------------------------------------------------------------
+| Function    : rd_free_database_partions
++------------------------------------------------------------------------------
+| Description : The function rd_free_database_partions() frees the partions,
+|               which are stored in the RD database. They are not linked to a
+|               LLC pdu because data blocks are not received in sequence. 
+|               Partions are removed from left window size (vq) up to the 
+|               right window size (vr).
+| Parameters  :  
+|               
++------------------------------------------------------------------------------
+*/
+GLOBAL void rd_free_database_partions ( void )
+{
+  USHORT bsn;
+
+  UBYTE  cnt=0;
+
+  T_NEXT_ARRAY  *help = NULL;
+
+  TRACE_FUNCTION( "rd_free_database_partions" );
+
+  bsn = grlc_data->rd.vq;  
+
+  while (bsn NEQ grlc_data->rd.vr) 
+  {
+    help = grlc_data->rd.data_array[bsn & WIN_MOD].first; 
+#ifdef _SIMULATION_
+    TRACE_EVENT_P2("candiate bsn=%d  vn=%d",bsn,grlc_data->rd.vn[bsn & WIN_MOD]);
+#endif /* _SIMULATION_*/
+
+    while (help != NULL) 
+    {
+      grlc_data->rd.data_array[bsn & WIN_MOD].first = 
+        (T_NEXT_ARRAY *)grlc_data->rd.data_array[bsn & WIN_MOD].first->next;
+
+      MFREE(help);
+
+      cnt++;
+#ifdef _SIMULATION_
+      TRACE_EVENT_P1("element free : bsn=%d",bsn);
+#endif /* _SIMULATION_*/
+      if(grlc_data->rd.data_array[bsn & WIN_MOD].pdu_cnt)
+		    help = grlc_data->rd.data_array[bsn & WIN_MOD].first;
+	    else
+		    help = NULL;
+	  }
+    grlc_data->rd.data_array[bsn & WIN_MOD].pdu_complete = FALSE;
+    grlc_data->rd.data_array[bsn & WIN_MOD].pdu_cnt      = 0xFF;
+    grlc_data->rd.data_array[bsn & WIN_MOD].first        = NULL;   
+    
+    bsn = (bsn + 1) & 0x7F;
+  }
+  TRACE_EVENT_P2("rd_free_database_partions after:  bsn=%d  cnt=%d ", bsn,  cnt);
+
+
+} /* rd_free_database_partions() */
+
+
+/*
++------------------------------------------------------------------------------
+| Function    : rd_cgrlc_st_time_ind 
++------------------------------------------------------------------------------
+| Description : The function rd_cgrlc_st_time_ind () informs higher layers
+|               that the starting time is elapsed
+|
+| Parameters  : -
+|
++------------------------------------------------------------------------------
+*/
+GLOBAL void rd_cgrlc_st_time_ind  ( void )
+{ 
+  PALLOC(cgrlc_starting_time_ind,CGRLC_STARTING_TIME_IND); /* T_CGRLC_STARTING_TIME_IND */
+
+  TRACE_FUNCTION( "rd_cgrlc_st_time_ind " );
+
+  cgrlc_starting_time_ind->tbf_mode = CGRLC_TBF_MODE_DL;
+  cgrlc_starting_time_ind->tfi      = grlc_data->dl_tfi;
+ 
+  PSEND(hCommGRR,cgrlc_starting_time_ind);
+
+  grlc_data->dl_tn_mask = grlc_data->downlink_tbf.ts_mask;
+  
+
+} /* rd_cgrlc_st_time_ind () */