/*
* Copyright 2013 - 2019 Software Radio Systems Limited
*
* This file is part of srsLTE .
*
* srsLTE is free software : you can redistribute it and / or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation , either version 3 of
* the License , or ( at your option ) any later version .
*
* srsLTE is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU Affero General Public License for more details .
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top - level directory of this distribution
* and at http : //www.gnu.org/licenses/.
*
*/
# include "srslte/upper/rlc_um.h"
# include <sstream>
# include <srslte/upper/rlc_interface.h>
# include <srslte/upper/rlc_common.h>
# define RX_MOD_BASE(x) (((x)-vr_uh-cfg.rx_window_size)%cfg.rx_mod)
using namespace asn1 : : rrc ;
namespace srslte {
rlc_um : : rlc_um ( ) : lcid ( 0 ) , tx ( ) , pool ( byte_buffer_pool : : get_instance ( ) ) , rrc ( NULL ) , log ( NULL )
{
bzero ( & cfg , sizeof ( srslte_rlc_um_config_t ) ) ;
}
// Warning: must call stop() to properly deallocate all buffers
rlc_um : : ~ rlc_um ( )
{
stop ( ) ;
}
void rlc_um : : init ( srslte : : log * log_ ,
uint32_t lcid_ ,
srsue : : pdcp_interface_rlc * pdcp_ ,
srsue : : rrc_interface_rlc * rrc_ ,
srslte : : mac_interface_timers * mac_timers_ )
{
tx . init ( log_ ) ;
rx . init ( log_ , lcid_ , pdcp_ , rrc_ , mac_timers_ ) ;
lcid = lcid_ ;
rrc = rrc_ ; // needed to determine bearer name during configuration
log = log_ ;
}
bool rlc_um : : configure ( srslte_rlc_config_t cnfg_ )
{
// determine bearer name and configure Rx/Tx objects
rb_name = get_rb_name ( rrc , lcid , cnfg_ . um . is_mrb ) ;
if ( not rx . configure ( cnfg_ , rb_name ) ) {
return false ;
}
if ( not tx . configure ( cnfg_ , rb_name ) ) {
return false ;
}
// store config
cfg = cnfg_ . um ;
log - > warning ( " %s configured in %s: t_reordering=%d ms, rx_sn_field_length=%u bits, tx_sn_field_length=%u bits \n " ,
rb_name . c_str ( ) , rlc_mode_text [ cnfg_ . rlc_mode ] , cfg . t_reordering ,
rlc_umd_sn_size_num [ cfg . rx_sn_field_length ] , rlc_umd_sn_size_num [ cfg . rx_sn_field_length ] ) ;
return true ;
}
bool rlc_um : : rlc_um_rx : : configure ( srslte_rlc_config_t cnfg_ , std : : string rb_name_ )
{
cfg = cnfg_ . um ;
if ( cfg . rx_mod = = 0 ) {
log - > error ( " Error configuring %s RLC UM: rx_mod==0 \n " , get_rb_name ( ) ) ;
return false ;
}
// set reordering timer
if ( reordering_timer ! = NULL ) {
reordering_timer - > set ( this , cfg . t_reordering ) ;
}
rb_name = rb_name_ ;
rx_enabled = true ;
return true ;
}
void rlc_um : : empty_queue ( ) {
// Drop all messages in TX SDU queue
tx . empty_queue ( ) ;
}
bool rlc_um : : is_mrb ( )
{
return cfg . is_mrb ;
}
void rlc_um : : reestablish ( )
{
tx . reestablish ( ) ; // calls stop and enables tx again
rx . reestablish ( ) ; // nothing else needed
}
void rlc_um : : stop ( )
{
tx . stop ( ) ;
rx . stop ( ) ;
}
rlc_mode_t rlc_um : : get_mode ( )
{
return RLC_MODE_UM ;
}
uint32_t rlc_um : : get_bearer ( )
{
return lcid ;
}
/****************************************************************************
* PDCP interface
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void rlc_um : : write_sdu ( byte_buffer_t * sdu , bool blocking )
{
if ( blocking ) {
tx . write_sdu ( sdu ) ;
} else {
tx . try_write_sdu ( sdu ) ;
}
}
/****************************************************************************
* MAC interface
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
bool rlc_um : : has_data ( )
{
return tx . has_data ( ) ;
}
uint32_t rlc_um : : get_buffer_state ( )
{
return tx . get_buffer_state ( ) ;
}
int rlc_um : : read_pdu ( uint8_t * payload , uint32_t nof_bytes )
{
return tx . build_data_pdu ( payload , nof_bytes ) ;
}
void rlc_um : : write_pdu ( uint8_t * payload , uint32_t nof_bytes )
{
rx . handle_data_pdu ( payload , nof_bytes ) ;
}
uint32_t rlc_um : : get_num_tx_bytes ( )
{
return tx . get_num_tx_bytes ( ) ;
}
uint32_t rlc_um : : get_num_rx_bytes ( )
{
return rx . get_num_rx_bytes ( ) ;
}
void rlc_um : : reset_metrics ( )
{
tx . reset_metrics ( ) ;
rx . reset_metrics ( ) ;
}
/****************************************************************************
* Helper functions
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
std : : string rlc_um : : get_rb_name ( srsue : : rrc_interface_rlc * rrc , uint32_t lcid , bool is_mrb )
{
if ( is_mrb ) {
std : : stringstream ss ;
ss < < " MRB " < < lcid ;
return ss . str ( ) ;
} else {
return rrc - > get_rb_name ( lcid ) ;
}
}
/****************************************************************************
* Tx subclass implementation
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
rlc_um : : rlc_um_tx : : rlc_um_tx ( ) :
pool ( byte_buffer_pool : : get_instance ( ) ) ,
log ( NULL ) ,
tx_sdu ( NULL ) ,
vt_us ( 0 ) ,
tx_enabled ( false ) ,
num_tx_bytes ( 0 )
{
pthread_mutex_init ( & mutex , NULL ) ;
}
rlc_um : : rlc_um_tx : : ~ rlc_um_tx ( )
{
pthread_mutex_destroy ( & mutex ) ;
}
void rlc_um : : rlc_um_tx : : init ( srslte : : log * log_ )
{
log = log_ ;
}
bool rlc_um : : rlc_um_tx : : configure ( srslte_rlc_config_t cnfg_ , std : : string rb_name_ )
{
cfg = cnfg_ . um ;
if ( cfg . tx_mod = = 0 ) {
log - > error ( " Error configuring %s RLC UM: tx_mod==0 \n " , get_rb_name ( ) ) ;
return false ;
}
tx_sdu_queue . resize ( cnfg_ . tx_queue_length ) ;
rb_name = rb_name_ ;
tx_enabled = true ;
return true ;
}
void rlc_um : : rlc_um_tx : : stop ( )
{
tx_enabled = false ;
empty_queue ( ) ;
}
void rlc_um : : rlc_um_tx : : reestablish ( )
{
stop ( ) ;
tx_enabled = true ;
}
void rlc_um : : rlc_um_tx : : empty_queue ( )
{
pthread_mutex_lock ( & mutex ) ;
// deallocate all SDUs in transmit queue
while ( tx_sdu_queue . size ( ) > 0 ) {
byte_buffer_t * buf ;
tx_sdu_queue . read ( & buf ) ;
pool - > deallocate ( buf ) ;
}
// deallocate SDU that is currently processed
if ( tx_sdu ) {
pool - > deallocate ( tx_sdu ) ;
tx_sdu = NULL ;
}
pthread_mutex_unlock ( & mutex ) ;
}
uint32_t rlc_um : : rlc_um_tx : : get_num_tx_bytes ( )
{
return num_tx_bytes ;
}
void rlc_um : : rlc_um_tx : : reset_metrics ( )
{
pthread_mutex_lock ( & mutex ) ;
num_tx_bytes = 0 ;
pthread_mutex_unlock ( & mutex ) ;
}
bool rlc_um : : rlc_um_tx : : has_data ( )
{
return ( tx_sdu ! = NULL | | ! tx_sdu_queue . is_empty ( ) ) ;
}
uint32_t rlc_um : : rlc_um_tx : : get_buffer_state ( )
{
// Bytes needed for tx SDUs
uint32_t n_sdus = tx_sdu_queue . size ( ) ;
uint32_t n_bytes = tx_sdu_queue . size_bytes ( ) ;
if ( tx_sdu ) {
n_sdus + + ;
n_bytes + = tx_sdu - > N_bytes ;
}
// Room needed for header extensions? (integer rounding)
if ( n_sdus > 1 ) {
n_bytes + = ( ( n_sdus - 1 ) * 1.5 ) + 0.5 ;
}
// Room needed for fixed header?
if ( n_bytes > 0 )
n_bytes + = ( cfg . is_mrb ) ? 2 : 3 ;
return n_bytes ;
}
void rlc_um : : rlc_um_tx : : write_sdu ( byte_buffer_t * sdu )
{
if ( ! tx_enabled ) {
byte_buffer_pool : : get_instance ( ) - > deallocate ( sdu ) ;
return ;
}
if ( sdu ) {
tx_sdu_queue . write ( sdu ) ;
log - > info_hex ( sdu - > msg , sdu - > N_bytes , " %s Tx SDU (%d B, tx_sdu_queue_len=%d) " , get_rb_name ( ) , sdu - > N_bytes , tx_sdu_queue . size ( ) ) ;
} else {
log - > warning ( " NULL SDU pointer in write_sdu() \n " ) ;
}
}
void rlc_um : : rlc_um_tx : : try_write_sdu ( byte_buffer_t * sdu )
{
if ( ! tx_enabled ) {
byte_buffer_pool : : get_instance ( ) - > deallocate ( sdu ) ;
return ;
}
if ( sdu ) {
if ( tx_sdu_queue . try_write ( sdu ) ) {
log - > info_hex ( sdu - > msg , sdu - > N_bytes , " %s Tx SDU (%d B, tx_sdu_queue_len=%d) " , get_rb_name ( ) , sdu - > N_bytes , tx_sdu_queue . size ( ) ) ;
} else {
log - > info_hex ( sdu - > msg , sdu - > N_bytes , " [Dropped SDU] %s Tx SDU (%d B, tx_sdu_queue_len=%d) " , get_rb_name ( ) , sdu - > N_bytes , tx_sdu_queue . size ( ) ) ;
pool - > deallocate ( sdu ) ;
}
} else {
log - > warning ( " NULL SDU pointer in write_sdu() \n " ) ;
}
}
int rlc_um : : rlc_um_tx : : build_data_pdu ( uint8_t * payload , uint32_t nof_bytes )
{
pthread_mutex_lock ( & mutex ) ;
log - > debug ( " MAC opportunity - %d bytes \n " , nof_bytes ) ;
if ( not tx_enabled ) {
pthread_mutex_unlock ( & mutex ) ;
return 0 ;
}
if ( ! tx_sdu & & tx_sdu_queue . size ( ) = = 0 ) {
log - > info ( " No data available to be sent \n " ) ;
pthread_mutex_unlock ( & mutex ) ;
return 0 ;
}
byte_buffer_t * pdu = pool_allocate ;
if ( ! pdu | | pdu - > N_bytes ! = 0 ) {
log - > error ( " Failed to allocate PDU buffer \n " ) ;
pthread_mutex_unlock ( & mutex ) ;
return 0 ;
}
rlc_umd_pdu_header_t header ;
header . fi = RLC_FI_FIELD_START_AND_END_ALIGNED ;
header . sn = vt_us ;
header . N_li = 0 ;
header . sn_size = cfg . tx_sn_field_length ;
uint32_t to_move = 0 ;
uint32_t last_li = 0 ;
uint8_t * pdu_ptr = pdu - > msg ;
int head_len = rlc_um_packed_length ( & header ) ;
int pdu_space = SRSLTE_MIN ( nof_bytes , pdu - > get_tailroom ( ) ) ; ;
if ( pdu_space < = head_len + 1 )
{
pool - > deallocate ( pdu ) ;
log - > warning ( " %s Cannot build a PDU - %d bytes available, %d bytes required for header \n " ,
get_rb_name ( ) , nof_bytes , head_len ) ;
pthread_mutex_unlock ( & mutex ) ;
return 0 ;
}
// Check for SDU segment
if ( tx_sdu ) {
uint32_t space = pdu_space - head_len ;
to_move = space > = tx_sdu - > N_bytes ? tx_sdu - > N_bytes : space ;
log - > debug ( " %s adding remainder of SDU segment - %d bytes of %d remaining \n " ,
get_rb_name ( ) , to_move , tx_sdu - > N_bytes ) ;
memcpy ( pdu_ptr , tx_sdu - > msg , to_move ) ;
last_li = to_move ;
pdu_ptr + = to_move ;
pdu - > N_bytes + = to_move ;
tx_sdu - > N_bytes - = to_move ;
tx_sdu - > msg + = to_move ;
if ( tx_sdu - > N_bytes = = 0 )
{
log - > debug ( " %s Complete SDU scheduled for tx. Stack latency: %ld us \n " ,
get_rb_name ( ) , tx_sdu - > get_latency_us ( ) ) ;
pool - > deallocate ( tx_sdu ) ;
tx_sdu = NULL ;
}
pdu_space - = SRSLTE_MIN ( to_move , pdu - > get_tailroom ( ) ) ;
header . fi | = RLC_FI_FIELD_NOT_START_ALIGNED ; // First byte does not correspond to first byte of SDU
}
// Pull SDUs from queue
while ( pdu_space > head_len + 1 & & tx_sdu_queue . size ( ) > 0 ) {
log - > debug ( " pdu_space=%d, head_len=%d \n " , pdu_space , head_len ) ;
if ( last_li > 0 )
header . li [ header . N_li + + ] = last_li ;
head_len = rlc_um_packed_length ( & header ) ;
tx_sdu_queue . read ( & tx_sdu ) ;
uint32_t space = pdu_space - head_len ;
to_move = space > = tx_sdu - > N_bytes ? tx_sdu - > N_bytes : space ;
log - > debug ( " %s adding new SDU segment - %d bytes of %d remaining \n " ,
get_rb_name ( ) , to_move , tx_sdu - > N_bytes ) ;
memcpy ( pdu_ptr , tx_sdu - > msg , to_move ) ;
last_li = to_move ;
pdu_ptr + = to_move ;
pdu - > N_bytes + = to_move ;
tx_sdu - > N_bytes - = to_move ;
tx_sdu - > msg + = to_move ;
if ( tx_sdu - > N_bytes = = 0 ) {
log - > debug ( " %s Complete SDU scheduled for tx. Stack latency: %ld us \n " ,
get_rb_name ( ) , tx_sdu - > get_latency_us ( ) ) ;
pool - > deallocate ( tx_sdu ) ;
tx_sdu = NULL ;
}
pdu_space - = to_move ;
}
if ( tx_sdu ) {
header . fi | = RLC_FI_FIELD_NOT_END_ALIGNED ; // Last byte does not correspond to last byte of SDU
}
// Set SN
header . sn = vt_us ;
vt_us = ( vt_us + 1 ) % cfg . tx_mod ;
// Add header and TX
rlc_um_write_data_pdu_header ( & header , pdu ) ;
memcpy ( payload , pdu - > msg , pdu - > N_bytes ) ;
uint32_t ret = pdu - > N_bytes ;
log - > info_hex ( payload , ret , " %s Tx PDU SN=%d (%d B) \n " , get_rb_name ( ) , header . sn , pdu - > N_bytes ) ;
pool - > deallocate ( pdu ) ;
debug_state ( ) ;
num_tx_bytes + = ret ;
pthread_mutex_unlock ( & mutex ) ;
return ret ;
}
const char * rlc_um : : rlc_um_tx : : get_rb_name ( )
{
return rb_name . c_str ( ) ;
}
void rlc_um : : rlc_um_tx : : debug_state ( )
{
log - > debug ( " %s vt_us = %d \n " , get_rb_name ( ) , vt_us ) ;
}
/****************************************************************************
* Rx subclass implementation
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
rlc_um : : rlc_um_rx : : rlc_um_rx ( )
: reordering_timer ( NULL )
, reordering_timer_id ( 0 )
, pool ( byte_buffer_pool : : get_instance ( ) )
, log ( NULL )
, pdcp ( NULL )
, rrc ( NULL )
, rx_sdu ( NULL )
, vr_ur ( 0 )
, vr_ux ( 0 )
, vr_uh ( 0 )
, vr_ur_in_rx_sdu ( 0 )
, pdu_lost ( false )
, mac_timers ( NULL )
, lcid ( 0 )
, num_rx_bytes ( 0 )
, rx_enabled ( false )
{
pthread_mutex_init ( & mutex , NULL ) ;
}
rlc_um : : rlc_um_rx : : ~ rlc_um_rx ( )
{
pthread_mutex_destroy ( & mutex ) ;
}
void rlc_um : : rlc_um_rx : : init ( srslte : : log * log_ , uint32_t lcid_ , srsue : : pdcp_interface_rlc * pdcp_ , srsue : : rrc_interface_rlc * rrc_ , srslte : : mac_interface_timers * mac_timers_ )
{
log = log_ ;
lcid = lcid_ ;
pdcp = pdcp_ ;
rrc = rrc_ ;
mac_timers = mac_timers_ ;
reordering_timer_id = mac_timers - > timer_get_unique_id ( ) ;
reordering_timer = mac_timers - > timer_get ( reordering_timer_id ) ;
}
void rlc_um : : rlc_um_rx : : reestablish ( )
{
// try to reassemble any SDUs if possible
if ( reordering_timer ! = NULL ) {
if ( reordering_timer - > is_running ( ) ) {
reordering_timer - > stop ( ) ;
timer_expired ( reordering_timer_id ) ;
}
}
pthread_mutex_lock ( & mutex ) ;
reset ( ) ;
rx_enabled = true ;
pthread_mutex_unlock ( & mutex ) ;
}
void rlc_um : : rlc_um_rx : : stop ( )
{
pthread_mutex_lock ( & mutex ) ;
reset ( ) ;
if ( mac_timers ! = NULL & & reordering_timer ! = NULL ) {
reordering_timer - > stop ( ) ;
mac_timers - > timer_release_id ( reordering_timer_id ) ;
reordering_timer = NULL ;
}
pthread_mutex_unlock ( & mutex ) ;
}
void rlc_um : : rlc_um_rx : : reset ( )
{
vr_ur = 0 ;
vr_ux = 0 ;
vr_uh = 0 ;
pdu_lost = false ;
rx_enabled = false ;
if ( rx_sdu ) {
pool - > deallocate ( rx_sdu ) ;
rx_sdu = NULL ;
}
// Drop all messages in RX window
std : : map < uint32_t , rlc_umd_pdu_t > : : iterator it ;
for ( it = rx_window . begin ( ) ; it ! = rx_window . end ( ) ; it + + ) {
pool - > deallocate ( it - > second . buf ) ;
}
rx_window . clear ( ) ;
}
void rlc_um : : rlc_um_rx : : handle_data_pdu ( uint8_t * payload , uint32_t nof_bytes )
{
pthread_mutex_lock ( & mutex ) ;
rlc_umd_pdu_t pdu ;
int header_len = 0 ;
std : : map < uint32_t , rlc_umd_pdu_t > : : iterator it ;
rlc_umd_pdu_header_t header ;
if ( ! rx_enabled ) {
goto unlock_and_exit ;
}
num_rx_bytes + = nof_bytes ;
rlc_um_read_data_pdu_header ( payload , nof_bytes , cfg . rx_sn_field_length , & header ) ;
log - > info_hex ( payload , nof_bytes , " RX %s Rx data PDU SN: %d (%d B) " , get_rb_name ( ) , header . sn , nof_bytes ) ;
if ( RX_MOD_BASE ( header . sn ) > = RX_MOD_BASE ( vr_uh - cfg . rx_window_size ) & &
RX_MOD_BASE ( header . sn ) < RX_MOD_BASE ( vr_ur ) )
{
log - > info ( " %s SN: %d outside rx window [%d:%d] - discarding \n " ,
get_rb_name ( ) , header . sn , vr_ur , vr_uh ) ;
goto unlock_and_exit ;
}
it = rx_window . find ( header . sn ) ;
if ( rx_window . end ( ) ! = it )
{
log - > info ( " %s Discarding duplicate SN: %d \n " , get_rb_name ( ) , header . sn ) ;
goto unlock_and_exit ;
}
// Write to rx window
pdu . buf = pool_allocate ;
if ( ! pdu . buf ) {
log - > error ( " Discarting packet: no space in buffer pool \n " ) ;
goto unlock_and_exit ;
}
memcpy ( pdu . buf - > msg , payload , nof_bytes ) ;
pdu . buf - > N_bytes = nof_bytes ;
//Strip header from PDU
header_len = rlc_um_packed_length ( & header ) ;
pdu . buf - > msg + = header_len ;
pdu . buf - > N_bytes - = header_len ;
pdu . header = header ;
rx_window [ header . sn ] = pdu ;
// Update vr_uh
if ( ! inside_reordering_window ( header . sn ) ) {
vr_uh = ( header . sn + 1 ) % cfg . rx_mod ;
}
// Reassemble and deliver SDUs, while updating vr_ur
log - > debug ( " Entering Reassemble from received PDU \n " ) ;
reassemble_rx_sdus ( ) ;
log - > debug ( " Finished reassemble from received PDU \n " ) ;
// Update reordering variables and timers
if ( reordering_timer - > is_running ( ) ) {
if ( RX_MOD_BASE ( vr_ux ) < = RX_MOD_BASE ( vr_ur ) | |
( ! inside_reordering_window ( vr_ux ) & & vr_ux ! = vr_uh ) )
{
reordering_timer - > stop ( ) ;
}
}
if ( ! reordering_timer - > is_running ( ) ) {
if ( RX_MOD_BASE ( vr_uh ) > RX_MOD_BASE ( vr_ur ) ) {
reordering_timer - > reset ( ) ;
reordering_timer - > run ( ) ;
vr_ux = vr_uh ;
}
}
debug_state ( ) ;
unlock_and_exit :
pthread_mutex_unlock ( & mutex ) ;
}
// No locking required as only called from within handle_data_pdu and timer_expired which lock
void rlc_um : : rlc_um_rx : : reassemble_rx_sdus ( )
{
if ( ! rx_sdu ) {
rx_sdu = pool_allocate ;
if ( ! rx_sdu ) {
log - > error ( " Fatal Error: Couldn't allocate buffer in rlc_um::reassemble_rx_sdus(). \n " ) ;
return ;
}
}
// First catch up with lower edge of reordering window
while ( ! inside_reordering_window ( vr_ur ) )
{
log - > debug ( " SN=%d is not inside reordering windows \n " , vr_ur ) ;
if ( rx_window . end ( ) = = rx_window . find ( vr_ur ) )
{
log - > debug ( " SN=%d not in rx_window. Reset received SDU \n " , vr_ur ) ;
rx_sdu - > reset ( ) ;
} else {
// Handle any SDU segments
for ( uint32_t i = 0 ; i < rx_window [ vr_ur ] . header . N_li ; i + + )
{
int len = rx_window [ vr_ur ] . header . li [ i ] ;
log - > debug_hex ( rx_window [ vr_ur ] . buf - > msg , len , " Handling segment %d/%d of length %d B of SN=%d \n " , i + 1 , rx_window [ vr_ur ] . header . N_li , len , vr_ur ) ;
// Check if we received a middle or end segment
if ( rx_sdu - > N_bytes = = 0 & & i = = 0 & & ! rlc_um_start_aligned ( rx_window [ vr_ur ] . header . fi ) ) {
log - > warning ( " Dropping PDU %d due to lost start segment \n " , vr_ur ) ;
// Advance data pointers and continue with next segment
rx_window [ vr_ur ] . buf - > msg + = len ;
rx_window [ vr_ur ] . buf - > N_bytes - = len ;
rx_sdu - > reset ( ) ;
break ;
}
memcpy ( & rx_sdu - > msg [ rx_sdu - > N_bytes ] , rx_window [ vr_ur ] . buf - > msg , len ) ;
rx_sdu - > N_bytes + = len ;
rx_window [ vr_ur ] . buf - > msg + = len ;
rx_window [ vr_ur ] . buf - > N_bytes - = len ;
if ( ( pdu_lost & & ! rlc_um_start_aligned ( rx_window [ vr_ur ] . header . fi ) ) | | ( vr_ur ! = ( ( vr_ur_in_rx_sdu + 1 ) % cfg . rx_mod ) ) ) {
log - > warning ( " Dropping remainder of lost PDU (lower edge middle segments, vr_ur=%d, vr_ur_in_rx_sdu=%d) \n " , vr_ur , vr_ur_in_rx_sdu ) ;
rx_sdu - > reset ( ) ;
} else {
log - > info_hex ( rx_sdu - > msg , rx_sdu - > N_bytes , " %s Rx SDU vr_ur=%d, i=%d (lower edge middle segments) " , get_rb_name ( ) , vr_ur , i ) ;
rx_sdu - > set_timestamp ( ) ;
if ( cfg . is_mrb ) {
pdcp - > write_pdu_mch ( lcid , rx_sdu ) ;
} else {
pdcp - > write_pdu ( lcid , rx_sdu ) ;
}
rx_sdu = pool_allocate ;
if ( ! rx_sdu ) {
log - > error ( " Fatal Error: Couldn't allocate buffer in rlc_um::reassemble_rx_sdus(). \n " ) ;
return ;
}
}
pdu_lost = false ;
}
// Handle last segment
if ( rx_sdu - > N_bytes > 0 | | rlc_um_start_aligned ( rx_window [ vr_ur ] . header . fi ) ) {
log - > info ( " Writing last segment in SDU buffer. Lower edge vr_ur=%d, Buffer size=%d, segment size=%d \n " ,
vr_ur , rx_sdu - > N_bytes , rx_window [ vr_ur ] . buf - > N_bytes ) ;
memcpy ( & rx_sdu - > msg [ rx_sdu - > N_bytes ] , rx_window [ vr_ur ] . buf - > msg , rx_window [ vr_ur ] . buf - > N_bytes ) ;
rx_sdu - > N_bytes + = rx_window [ vr_ur ] . buf - > N_bytes ;
vr_ur_in_rx_sdu = vr_ur ;
if ( rlc_um_end_aligned ( rx_window [ vr_ur ] . header . fi ) )
{
if ( pdu_lost & & ! rlc_um_start_aligned ( rx_window [ vr_ur ] . header . fi ) ) {
log - > warning ( " Dropping remainder of lost PDU (lower edge last segments) \n " ) ;
rx_sdu - > reset ( ) ;
} else {
log - > info_hex ( rx_sdu - > msg , rx_sdu - > N_bytes , " %s Rx SDU vr_ur=%d (lower edge last segments) " , get_rb_name ( ) , vr_ur ) ;
rx_sdu - > set_timestamp ( ) ;
if ( cfg . is_mrb ) {
pdcp - > write_pdu_mch ( lcid , rx_sdu ) ;
} else {
pdcp - > write_pdu ( lcid , rx_sdu ) ;
}
rx_sdu = pool_allocate ;
if ( ! rx_sdu ) {
log - > error ( " Fatal Error: Couldn't allocate buffer in rlc_um::reassemble_rx_sdus(). \n " ) ;
return ;
}
}
pdu_lost = false ;
}
}
// Clean up rx_window
pool - > deallocate ( rx_window [ vr_ur ] . buf ) ;
rx_window . erase ( vr_ur ) ;
}
vr_ur = ( vr_ur + 1 ) % cfg . rx_mod ;
}
// Now update vr_ur until we reach an SN we haven't yet received
while ( rx_window . end ( ) ! = rx_window . find ( vr_ur ) ) {
log - > debug ( " Reassemble loop for vr_ur=%d \n " , vr_ur ) ;
if ( not pdu_belongs_to_rx_sdu ( ) ) {
log - > warning ( " PDU SN=%d lost, stop reassambling SDU (vr_ur_in_rx_sdu=%d) \n " , vr_ur_in_rx_sdu + 1 , vr_ur_in_rx_sdu ) ;
pdu_lost = false ; // Reset flag to not prevent reassembling of further segments
rx_sdu - > reset ( ) ;
}
// Handle any SDU segments
for ( uint32_t i = 0 ; i < rx_window [ vr_ur ] . header . N_li ; i + + ) {
uint16_t len = rx_window [ vr_ur ] . header . li [ i ] ;
log - > debug ( " Handling SDU segment i=%d with len=%d of vr_ur=%d N_li=%d [%s] \n " , i , len , vr_ur , rx_window [ vr_ur ] . header . N_li , rlc_fi_field_text [ rx_window [ vr_ur ] . header . fi ] ) ;
// Check if the first part of the PDU is a middle or end segment
if ( rx_sdu - > N_bytes = = 0 & & i = = 0 & & ! rlc_um_start_aligned ( rx_window [ vr_ur ] . header . fi ) ) {
log - > warning_hex ( rx_window [ vr_ur ] . buf - > msg , len , " Dropping first %d B of SN %d due to lost start segment \n " , len , vr_ur ) ;
if ( rx_window [ vr_ur ] . buf - > N_bytes < len ) {
log - > error ( " Dropping remaining remainder of SN %d too (N_bytes=%u < len=%d) \n " , vr_ur , rx_window [ vr_ur ] . buf - > N_bytes , len ) ;
goto clean_up_rx_window ;
}
// Advance data pointers and continue with next segment
rx_window [ vr_ur ] . buf - > msg + = len ;
rx_window [ vr_ur ] . buf - > N_bytes - = len ;
rx_sdu - > reset ( ) ;
// Reset flag, it is safe to process all remaining segments of this PDU
pdu_lost = false ;
continue ;
}
// Check available space in SDU
if ( ( uint32_t ) len > rx_sdu - > get_tailroom ( ) ) {
log - > error ( " Dropping PDU %d due to buffer mis-alignment (current segment len %d B, received %d B) \n " , vr_ur , rx_sdu - > N_bytes , len ) ;
rx_sdu - > reset ( ) ;
goto clean_up_rx_window ;
}
if ( not pdu_belongs_to_rx_sdu ( ) ) {
log - > info_hex ( rx_window [ vr_ur ] . buf - > msg , len , " Copying first %d bytes of new SDU \n " , len ) ;
log - > info ( " Updating vr_ur_in_rx_sdu. old=%d, new=%d \n " , vr_ur_in_rx_sdu , vr_ur ) ;
vr_ur_in_rx_sdu = vr_ur ;
} else {
log - > info_hex ( rx_window [ vr_ur ] . buf - > msg , len , " Concatenating %d bytes in to current length %d. rx_window remaining bytes=%d, vr_ur_in_rx_sdu=%d, vr_ur=%d, rx_mod=%d, last_mod=%d \n " ,
len , rx_sdu - > N_bytes , rx_window [ vr_ur ] . buf - > N_bytes , vr_ur_in_rx_sdu , vr_ur , cfg . rx_mod , ( vr_ur_in_rx_sdu + 1 ) % cfg . rx_mod ) ;
}
memcpy ( & rx_sdu - > msg [ rx_sdu - > N_bytes ] , rx_window [ vr_ur ] . buf - > msg , len ) ;
rx_sdu - > N_bytes + = len ;
rx_window [ vr_ur ] . buf - > msg + = len ;
rx_window [ vr_ur ] . buf - > N_bytes - = len ;
vr_ur_in_rx_sdu = vr_ur ;
if ( pdu_belongs_to_rx_sdu ( ) ) {
log - > info_hex ( rx_sdu - > msg , rx_sdu - > N_bytes , " %s Rx SDU vr_ur=%d, i=%d, (update vr_ur middle segments) " , get_rb_name ( ) , vr_ur , i ) ;
rx_sdu - > set_timestamp ( ) ;
if ( cfg . is_mrb ) {
pdcp - > write_pdu_mch ( lcid , rx_sdu ) ;
} else {
pdcp - > write_pdu ( lcid , rx_sdu ) ;
}
rx_sdu = pool_allocate ;
if ( ! rx_sdu ) {
log - > error ( " Fatal Error: Couldn't allocate buffer in rlc_um::reassemble_rx_sdus(). \n " ) ;
return ;
}
} else {
log - > warning ( " Dropping remainder of lost PDU (update vr_ur middle segments, vr_ur=%d, vr_ur_in_rx_sdu=%d) \n " , vr_ur , vr_ur_in_rx_sdu ) ;
// Advance data pointers and continue with next segment
rx_window [ vr_ur ] . buf - > msg + = len ;
rx_window [ vr_ur ] . buf - > N_bytes - = len ;
}
pdu_lost = false ;
}
// Handle last segment
if ( rx_sdu - > N_bytes = = 0 & & rx_window [ vr_ur ] . header . N_li = = 0 & & ! rlc_um_start_aligned ( rx_window [ vr_ur ] . header . fi ) ) {
log - > warning ( " Dropping PDU %d due to lost start segment \n " , vr_ur ) ;
rx_sdu - > reset ( ) ;
goto clean_up_rx_window ;
}
if ( rx_sdu - > N_bytes < SRSLTE_MAX_BUFFER_SIZE_BYTES & &
rx_window [ vr_ur ] . buf - > N_bytes < SRSLTE_MAX_BUFFER_SIZE_BYTES & &
rx_window [ vr_ur ] . buf - > N_bytes + rx_sdu - > N_bytes < SRSLTE_MAX_BUFFER_SIZE_BYTES )
{
log - > info_hex ( rx_window [ vr_ur ] . buf - > msg , rx_window [ vr_ur ] . buf - > N_bytes , " Writing last segment in SDU buffer. Updating vr_ur=%d, vr_ur_in_rx_sdu=%d, Buffer size=%d, segment size=%d \n " ,
vr_ur , vr_ur_in_rx_sdu , rx_sdu - > N_bytes , rx_window [ vr_ur ] . buf - > N_bytes ) ;
memcpy ( & rx_sdu - > msg [ rx_sdu - > N_bytes ] , rx_window [ vr_ur ] . buf - > msg , rx_window [ vr_ur ] . buf - > N_bytes ) ;
rx_sdu - > N_bytes + = rx_window [ vr_ur ] . buf - > N_bytes ;
} else {
log - > error ( " Out of bounds while reassembling SDU buffer in UM: sdu_len=%d, window_buffer_len=%d, vr_ur=%d \n " ,
rx_sdu - > N_bytes , rx_window [ vr_ur ] . buf - > N_bytes , vr_ur ) ;
}
vr_ur_in_rx_sdu = vr_ur ;
if ( rlc_um_end_aligned ( rx_window [ vr_ur ] . header . fi ) ) {
if ( pdu_lost & & ! rlc_um_start_aligned ( rx_window [ vr_ur ] . header . fi ) ) {
log - > warning ( " Dropping remainder of lost PDU (update vr_ur last segments) \n " ) ;
rx_sdu - > reset ( ) ;
} else {
log - > info_hex ( rx_sdu - > msg , rx_sdu - > N_bytes , " %s Rx SDU vr_ur=%d (update vr_ur last segments) " , get_rb_name ( ) , vr_ur ) ;
rx_sdu - > set_timestamp ( ) ;
if ( cfg . is_mrb ) {
pdcp - > write_pdu_mch ( lcid , rx_sdu ) ;
} else {
pdcp - > write_pdu ( lcid , rx_sdu ) ;
}
rx_sdu = pool_allocate ;
if ( ! rx_sdu ) {
log - > error ( " Fatal Error: Couldn't allocate buffer in rlc_um::reassemble_rx_sdus(). \n " ) ;
return ;
}
}
pdu_lost = false ;
}
clean_up_rx_window :
// Clean up rx_window
pool - > deallocate ( rx_window [ vr_ur ] . buf ) ;
rx_window . erase ( vr_ur ) ;
vr_ur = ( vr_ur + 1 ) % cfg . rx_mod ;
}
}
// Only called when lock is hold
bool rlc_um : : rlc_um_rx : : pdu_belongs_to_rx_sdu ( )
{
// return true if the currently received SDU
if ( ( ( vr_ur_in_rx_sdu + 1 ) % cfg . rx_mod = = vr_ur ) | | ( vr_ur = = vr_ur_in_rx_sdu ) ) {
return true ;
}
return false ;
}
// Only called when lock is hold
// 36.322 Section 5.1.2.2.1
bool rlc_um : : rlc_um_rx : : inside_reordering_window ( uint16_t sn )
{
if ( cfg . rx_window_size = = 0 | | rx_window . empty ( ) ) {
return true ;
}
if ( RX_MOD_BASE ( vr_uh - cfg . rx_window_size ) < = RX_MOD_BASE ( sn ) & & RX_MOD_BASE ( sn ) < RX_MOD_BASE ( vr_uh ) ) {
return true ;
} else {
return false ;
}
}
uint32_t rlc_um : : rlc_um_rx : : get_num_rx_bytes ( )
{
return num_rx_bytes ;
}
void rlc_um : : rlc_um_rx : : reset_metrics ( )
{
pthread_mutex_lock ( & mutex ) ;
num_rx_bytes = 0 ;
pthread_mutex_unlock ( & mutex ) ;
}
/****************************************************************************
* Timeout callback interface
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void rlc_um : : rlc_um_rx : : timer_expired ( uint32_t timeout_id )
{
pthread_mutex_lock ( & mutex ) ;
if ( reordering_timer ! = NULL & & reordering_timer_id = = timeout_id ) {
// 36.322 v10 Section 5.1.2.2.4
log - > info ( " %s reordering timeout expiry - updating vr_ur and reassembling \n " ,
get_rb_name ( ) ) ;
log - > warning ( " Lost PDU SN: %d \n " , vr_ur ) ;
pdu_lost = true ;
if ( rx_sdu ! = NULL ) {
rx_sdu - > reset ( ) ;
}
while ( RX_MOD_BASE ( vr_ur ) < RX_MOD_BASE ( vr_ux ) ) {
vr_ur = ( vr_ur + 1 ) % cfg . rx_mod ;
log - > debug ( " Entering Reassemble from timeout id=%d \n " , timeout_id ) ;
reassemble_rx_sdus ( ) ;
log - > debug ( " Finished reassemble from timeout id=%d \n " , timeout_id ) ;
}
if ( RX_MOD_BASE ( vr_uh ) > RX_MOD_BASE ( vr_ur ) ) {
reordering_timer - > reset ( ) ;
reordering_timer - > run ( ) ;
vr_ux = vr_uh ;
}
debug_state ( ) ;
}
pthread_mutex_unlock ( & mutex ) ;
}
/****************************************************************************
* Helper functions
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void rlc_um : : rlc_um_rx : : debug_state ( )
{
log - > debug ( " %s vr_ur = %d, vr_ux = %d, vr_uh = %d \n " , get_rb_name ( ) , vr_ur , vr_ux , vr_uh ) ;
}
const char * rlc_um : : rlc_um_rx : : get_rb_name ( )
{
return rb_name . c_str ( ) ;
}
/****************************************************************************
* Header pack / unpack helper functions
* Ref : 3 GPP TS 36.322 v10 .0 .0 Section 6.2 .1
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void rlc_um_read_data_pdu_header ( byte_buffer_t * pdu , rlc_umd_sn_size_t sn_size , rlc_umd_pdu_header_t * header )
{
rlc_um_read_data_pdu_header ( pdu - > msg , pdu - > N_bytes , sn_size , header ) ;
}
void rlc_um_read_data_pdu_header ( uint8_t * payload , uint32_t nof_bytes , rlc_umd_sn_size_t sn_size , rlc_umd_pdu_header_t * header )
{
uint8_t ext ;
uint8_t * ptr = payload ;
// Fixed part
if ( RLC_UMD_SN_SIZE_5_BITS = = sn_size )
{
header - > fi = ( rlc_fi_field_t ) ( ( * ptr > > 6 ) & 0x03 ) ; // 2 bits FI
ext = ( ( * ptr > > 5 ) & 0x01 ) ; // 1 bit EXT
header - > sn = * ptr & 0x1F ; // 5 bits SN
ptr + + ;
} else {
header - > fi = ( rlc_fi_field_t ) ( ( * ptr > > 3 ) & 0x03 ) ; // 2 bits FI
ext = ( ( * ptr > > 2 ) & 0x01 ) ; // 1 bit EXT
header - > sn = ( * ptr & 0x03 ) < < 8 ; // 2 bits SN
ptr + + ;
header - > sn | = ( * ptr & 0xFF ) ; // 8 bits SN
ptr + + ;
}
header - > sn_size = sn_size ;
// Extension part
header - > N_li = 0 ;
while ( ext )
{
if ( header - > N_li % 2 = = 0 )
{
ext = ( ( * ptr > > 7 ) & 0x01 ) ;
header - > li [ header - > N_li ] = ( * ptr & 0x7F ) < < 4 ; // 7 bits of LI
ptr + + ;
header - > li [ header - > N_li ] | = ( * ptr & 0xF0 ) > > 4 ; // 4 bits of LI
header - > N_li + + ;
}
else
{
ext = ( * ptr > > 3 ) & 0x01 ;
header - > li [ header - > N_li ] = ( * ptr & 0x07 ) < < 8 ; // 3 bits of LI
ptr + + ;
header - > li [ header - > N_li ] | = ( * ptr & 0xFF ) ; // 8 bits of LI
header - > N_li + + ;
ptr + + ;
}
}
}
void rlc_um_write_data_pdu_header ( rlc_umd_pdu_header_t * header , byte_buffer_t * pdu )
{
uint32_t i ;
uint8_t ext = ( header - > N_li > 0 ) ? 1 : 0 ;
// Make room for the header
uint32_t len = rlc_um_packed_length ( header ) ;
pdu - > msg - = len ;
uint8_t * ptr = pdu - > msg ;
// Fixed part
if ( RLC_UMD_SN_SIZE_5_BITS = = header - > sn_size )
{
* ptr = ( header - > fi & 0x03 ) < < 6 ; // 2 bits FI
* ptr | = ( ext & 0x01 ) < < 5 ; // 1 bit EXT
* ptr | = header - > sn & 0x1F ; // 5 bits SN
ptr + + ;
} else {
* ptr = ( header - > fi & 0x03 ) < < 3 ; // 3 Reserved bits | 2 bits FI
* ptr | = ( ext & 0x01 ) < < 2 ; // 1 bit EXT
* ptr | = ( header - > sn & 0x300 ) > > 8 ; // 2 bits SN
ptr + + ;
* ptr = ( header - > sn & 0xFF ) ; // 8 bits SN
ptr + + ;
}
// Extension part
i = 0 ;
while ( i < header - > N_li )
{
ext = ( ( i + 1 ) = = header - > N_li ) ? 0 : 1 ;
* ptr = ( ext & 0x01 ) < < 7 ; // 1 bit header
* ptr | = ( header - > li [ i ] & 0x7F0 ) > > 4 ; // 7 bits of LI
ptr + + ;
* ptr = ( header - > li [ i ] & 0x00F ) < < 4 ; // 4 bits of LI
i + + ;
if ( i < header - > N_li )
{
ext = ( ( i + 1 ) = = header - > N_li ) ? 0 : 1 ;
* ptr | = ( ext & 0x01 ) < < 3 ; // 1 bit header
* ptr | = ( header - > li [ i ] & 0x700 ) > > 8 ; // 3 bits of LI
ptr + + ;
* ptr = ( header - > li [ i ] & 0x0FF ) ; // 8 bits of LI
ptr + + ;
i + + ;
}
}
// Pad if N_li is odd
if ( header - > N_li % 2 = = 1 )
ptr + + ;
pdu - > N_bytes + = ptr - pdu - > msg ;
}
uint32_t rlc_um_packed_length ( rlc_umd_pdu_header_t * header )
{
uint32_t len = 0 ;
if ( RLC_UMD_SN_SIZE_5_BITS = = header - > sn_size )
{
len + = 1 ; // Fixed part is 1 byte
} else {
len + = 2 ; // Fixed part is 2 bytes
}
len + = header - > N_li * 1.5 + 0.5 ; // Extension part - integer rounding up
return len ;
}
bool rlc_um_start_aligned ( uint8_t fi )
{
return ( fi = = RLC_FI_FIELD_START_AND_END_ALIGNED | | fi = = RLC_FI_FIELD_NOT_END_ALIGNED ) ;
}
bool rlc_um_end_aligned ( uint8_t fi )
{
return ( fi = = RLC_FI_FIELD_START_AND_END_ALIGNED | | fi = = RLC_FI_FIELD_NOT_START_ALIGNED ) ;
}
} // namespace srsue