@ -322,7 +322,7 @@ uint32_t rlc_am::rlc_am_tx::get_buffer_state()
// Bytes needed for status report
if ( do_status ( ) & & not status_prohibited ) {
n_bytes = parent - > rx . get_status ( & tx_status ) ;
n_bytes = parent - > rx . get_status _pdu_length ( ) ;
log - > debug ( " %s Buffer state - status report: %d bytes \n " , RB_NAME , n_bytes ) ;
goto unlock_and_return ;
}
@ -378,7 +378,7 @@ uint32_t rlc_am::rlc_am_tx::get_total_buffer_state()
// Bytes needed for status report
if ( do_status ( ) & & not status_prohibited ) {
n_bytes + = parent - > rx . get_status ( & tx_status ) ;
n_bytes + = parent - > rx . get_status _pdu_length ( ) ;
log - > debug ( " %s Buffer state - total status report: %d bytes \n " , RB_NAME , n_bytes ) ;
}
@ -496,8 +496,10 @@ void rlc_am::rlc_am_tx::timer_expired(uint32_t timeout_id)
pthread_mutex_lock ( & mutex ) ;
if ( poll_retx_timer ! = NULL & & poll_retx_timer_id = = timeout_id ) {
log - > debug ( " Poll reTx timer expired for LCID=%d after %d ms \n " , parent - > lcid , poll_retx_timer - > get_timeout ( ) ) ;
// Section 5.2.2.3 in TS 36.311, if tx_window is full and retx_queue empty, retransmit random PDU
if ( ( tx_window . size ( ) > = RLC_AM_WINDOW_SIZE & & retx_queue . empty ( ) & & tx_sdu_queue . size ( ) = = 0 ) ) {
// Section 5.2.2.3 in TS 36.311, schedule random PDU for retransmission if
// (a) both tx and retx buffer are empty, or
// (b) no new data PDU can be transmitted (tx window is full)
if ( ( retx_queue . empty ( ) & & tx_sdu_queue . size ( ) = = 0 ) | | tx_window . size ( ) > = RLC_AM_WINDOW_SIZE ) {
retransmit_random_pdu ( ) ;
}
} else
@ -509,10 +511,10 @@ void rlc_am::rlc_am_tx::timer_expired(uint32_t timeout_id)
void rlc_am : : rlc_am_tx : : retransmit_random_pdu ( )
{
if ( not tx_window . empty ( ) ) {
// randomly select PDU in tx window for retransmission
std : : map < uint32_t , rlc_amd_tx_pdu_t > : : iterator it = tx_window . begin ( ) ;
std : : advance ( it , rand ( ) % tx_window . size ( ) ) ;
log - > info ( " Schedule SN=%d for reTx. \n " , it - > first ) ;
rlc_amd_retx_t retx = { } ;
retx . is_segment = false ;
@ -520,6 +522,7 @@ void rlc_am::rlc_am_tx::retransmit_random_pdu()
retx . so_end = it - > second . buf - > N_bytes ;
retx . sn = it - > first ;
retx_queue . push_back ( retx ) ;
}
}
uint32_t rlc_am : : rlc_am_tx : : get_num_tx_bytes ( )
@ -579,10 +582,11 @@ bool rlc_am::rlc_am_tx::poll_required()
int rlc_am : : rlc_am_tx : : build_status_pdu ( uint8_t * payload , uint32_t nof_bytes )
{
int pdu_len = parent - > rx . get_status ( & tx_status ) ;
int pdu_len = parent - > rx . get_status_pdu ( & tx_status , nof_bytes ) ;
log - > debug ( " %s \n " , rlc_am_status_pdu_to_string ( & tx_status ) . c_str ( ) ) ;
if ( pdu_len > 0 & & nof_bytes > = static_cast < uint32_t > ( pdu_len ) ) {
log - > info ( " %s Tx status PDU - %s \n " ,
RB_NAME , rlc_am_ to_string( & tx_status ) . c_str ( ) ) ;
RB_NAME , rlc_am_ status_pdu_ to_string( & tx_status ) . c_str ( ) ) ;
parent - > rx . reset_status ( ) ;
@ -757,6 +761,10 @@ int rlc_am::rlc_am_tx::build_segment(uint8_t *payload, uint32_t nof_bytes, rlc_a
upper + = old_header . li [ i ] ;
head_len = rlc_am_packed_length ( & new_header ) ;
// Accomodate some extra space for for LIs if old header contained segments too
head_len + = old_header . N_li ;
pdu_space = nof_bytes - head_len ;
if ( pdu_space < ( retx . so_end - retx . so_start ) ) {
retx . so_end = retx . so_start + pdu_space ;
@ -776,17 +784,15 @@ int rlc_am::rlc_am_tx::build_segment(uint8_t *payload, uint32_t nof_bytes, rlc_a
if ( upper = = retx . so_end ) {
new_header . fi & = RLC_FI_FIELD_NOT_START_ALIGNED ; // segment end is aligned with this SDU
}
new_header . li [ new_header . N_li + + ] = li ;
}
new_header . li [ new_header . N_li ] = li ;
lower + = old_header . li [ i ] ;
// only increment N_li if more SDU (segments) are being added
if ( retx . so_end > upper ) {
new_header . N_li + + ;
}
}
// Make sure LI is not deleted in case the SDU boundary is crossed
// FIXME: fix if N_li > 1
if ( new_header . N_li = = 1 & & retx . so_start + new_header . li [ 0 ] < retx . so_end & & retx . so_end < = retx . so_start + pdu_space ) {
// This segment crosses a SDU boundary
new_header . N_li + + ;
lower + = old_header . li [ i ] ;
}
// Update retx_queue
@ -801,9 +807,6 @@ int rlc_am::rlc_am_tx::build_segment(uint8_t *payload, uint32_t nof_bytes, rlc_a
} else {
retx_queue . front ( ) . is_segment = true ;
retx_queue . front ( ) . so_start = retx . so_end ;
if ( new_header . N_li > 0 ) {
new_header . N_li - - ;
}
}
// Write header and pdu
@ -813,9 +816,6 @@ int rlc_am::rlc_am_tx::build_segment(uint8_t *payload, uint32_t nof_bytes, rlc_a
uint32_t len = retx . so_end - retx . so_start ;
memcpy ( ptr , data , len ) ;
log - > info ( " %s Retx PDU segment scheduled for tx. SN: %d, SO: %d \n " ,
RB_NAME , retx . sn , retx . so_start ) ;
debug_state ( ) ;
int pdu_len = ( ptr - payload ) + len ;
if ( pdu_len > static_cast < int > ( nof_bytes ) ) {
@ -824,14 +824,17 @@ int rlc_am::rlc_am_tx::build_segment(uint8_t *payload, uint32_t nof_bytes, rlc_a
log - > debug ( " %s Retx PDU segment length error. Header len: %ld, Payload len: %d, N_li: %d \n " ,
RB_NAME , ( ptr - payload ) , len , new_header . N_li ) ;
}
log - > info_hex ( payload , pdu_len , " %s Retx PDU segment of SN=%d (%d B), SO: %d, N_li: %d \n " ,
RB_NAME , retx . sn , pdu_len , retx . so_start , new_header . N_li ) ;
return pdu_len ;
}
int rlc_am : : rlc_am_tx : : build_data_pdu ( uint8_t * payload , uint32_t nof_bytes )
{
if ( tx_sdu = = NULL & & tx_sdu_queue . size ( ) = = 0 )
{
if ( tx_sdu = = NULL & & tx_sdu_queue . size ( ) = = 0 ) {
log - > info ( " No data available to be sent \n " ) ;
return 0 ;
}
@ -875,7 +878,7 @@ int rlc_am::rlc_am_tx::build_data_pdu(uint8_t *payload, uint32_t nof_bytes)
uint32_t head_len = rlc_am_packed_length ( & header ) ;
uint32_t to_move = 0 ;
uint32_t last_li = 0 ;
uint32_t pdu_space = nof_bytes;
uint32_t pdu_space = SRSLTE_MIN( nof_bytes, pdu - > get_tailroom ( ) ) ;
uint8_t * pdu_ptr = pdu - > msg ;
if ( pdu_space < = head_len + 1 )
@ -906,7 +909,7 @@ int rlc_am::rlc_am_tx::build_data_pdu(uint8_t *payload, uint32_t nof_bytes)
tx_sdu = NULL ;
}
if ( pdu_space > to_move ) {
pdu_space - = to_move;
pdu_space - = SRSLTE_MIN( to_move, pdu - > get_tailroom ( ) ) ;
} else {
pdu_space = 0 ;
}
@ -917,9 +920,10 @@ int rlc_am::rlc_am_tx::build_data_pdu(uint8_t *payload, uint32_t nof_bytes)
}
// Pull SDUs from queue
while ( pdu_space > head_len + 1 & & tx_sdu_queue . size ( ) > 0 ) {
while ( pdu_space > head_len + 1 & & tx_sdu_queue . size ( ) > 0 & & header . N_li < RLC_AM_WINDOW_SIZE ) {
if ( last_li > 0 ) {
header . li [ header . N_li + + ] = last_li ;
header . li [ header . N_li ] = last_li ;
header . N_li + + ;
}
head_len = rlc_am_packed_length ( & header ) ;
if ( head_len > = pdu_space ) {
@ -934,8 +938,7 @@ int rlc_am::rlc_am_tx::build_data_pdu(uint8_t *payload, uint32_t nof_bytes)
pdu - > N_bytes + = to_move ;
tx_sdu - > N_bytes - = to_move ;
tx_sdu - > msg + = to_move ;
if ( tx_sdu - > N_bytes = = 0 )
{
if ( tx_sdu - > N_bytes = = 0 ) {
log - > debug ( " %s Complete SDU scheduled for tx. Stack latency: %ld us \n " ,
RB_NAME , tx_sdu - > get_latency_us ( ) ) ;
pool - > deallocate ( tx_sdu ) ;
@ -992,10 +995,11 @@ int rlc_am::rlc_am_tx::build_data_pdu(uint8_t *payload, uint32_t nof_bytes)
uint8_t * ptr = payload ;
rlc_am_write_data_pdu_header ( & header , & ptr ) ;
memcpy ( ptr , pdu - > msg , pdu - > N_bytes ) ;
log - > info_hex ( payload , pdu - > N_bytes , " %s PDU scheduled for tx. SN: %d (%d B) \n " , RB_NAME , header . sn , pdu - > N_bytes ) ;
int total_len = ( ptr - payload ) + pdu - > N_bytes ;
log - > info_hex ( payload , total_len , " %s Tx PDU SN=%d (%d B) \n " , RB_NAME , header . sn , total_len ) ;
log - > debug ( " %s \n " , rlc_amd_pdu_header_to_string ( header ) . c_str ( ) ) ;
debug_state ( ) ;
return ( ptr - payload ) + pdu - > N_bytes ;
return total_len ;
}
void rlc_am : : rlc_am_tx : : handle_control_pdu ( uint8_t * payload , uint32_t nof_bytes )
@ -1007,10 +1011,10 @@ void rlc_am::rlc_am_tx::handle_control_pdu(uint8_t *payload, uint32_t nof_bytes)
rlc_status_pdu_t status ;
rlc_am_read_status_pdu ( payload , nof_bytes , & status ) ;
log - > info ( " %s Rx Status PDU: %s \n " , RB_NAME , rlc_am_ to_string( & status ) . c_str ( ) ) ;
log - > info ( " %s Rx Status PDU: %s \n " , RB_NAME , rlc_am_ status_pdu_ to_string( & status ) . c_str ( ) ) ;
if ( poll_retx_timer ! = NULL ) {
poll_retx_timer - > re se t( ) ;
poll_retx_timer - > stop ( ) ;
}
// flush retx queue to avoid unordered SNs, we expect the Rx to request lost PDUs again
@ -1304,11 +1308,11 @@ void rlc_am::rlc_am_rx::handle_data_pdu(uint8_t *payload, uint32_t nof_bytes, rl
{
std : : map < uint32_t , rlc_amd_rx_pdu_t > : : iterator it ;
log - > info_hex ( payload , nof_bytes , " %s Rx data PDU SN : %d (%d B), %s " ,
log - > info_hex ( payload , nof_bytes , " %s Rx data PDU SN =%d (%d B) " ,
RB_NAME ,
header . sn ,
nof_bytes ,
rlc_fi_field_text [ header . fi ] ) ;
nof_bytes );
log - > debug ( " %s \n " , rlc_amd_pdu_header_to_string ( header ) . c_str ( ) ) ;
if ( ! inside_rx_window ( header . sn ) ) {
if ( header . p ) {
@ -1375,9 +1379,7 @@ void rlc_am::rlc_am_rx::handle_data_pdu(uint8_t *payload, uint32_t nof_bytes, rl
poll_received = true ;
// 36.322 v10 Section 5.2.3
if ( RX_MOD_BASE ( header . sn ) < RX_MOD_BASE ( vr_ms ) | |
RX_MOD_BASE ( header . sn ) > = RX_MOD_BASE ( vr_mr ) )
{
if ( RX_MOD_BASE ( header . sn ) < RX_MOD_BASE ( vr_ms ) | | RX_MOD_BASE ( header . sn ) > = RX_MOD_BASE ( vr_mr ) ) {
do_status = true ;
}
// else delay for reordering timer
@ -1390,7 +1392,7 @@ void rlc_am::rlc_am_rx::handle_data_pdu(uint8_t *payload, uint32_t nof_bytes, rl
if ( reordering_timer ! = NULL ) {
if ( reordering_timer - > is_running ( ) ) {
if ( vr_x = = vr_r | | ( ! inside_rx_window ( vr_x ) & & vr_x ! = vr_mr ) ) {
reordering_timer - > re se t( ) ;
reordering_timer - > stop ( ) ;
}
}
@ -1411,8 +1413,9 @@ void rlc_am::rlc_am_rx::handle_data_pdu_segment(uint8_t *payload, uint32_t nof_b
{
std : : map < uint32_t , rlc_amd_rx_pdu_segments_t > : : iterator it ;
log - > info_hex ( payload , nof_bytes , " %s Rx data PDU segment. SN: %d, SO: %d " ,
RB_NAME , header . sn , header . so ) ;
log - > info_hex ( payload , nof_bytes , " %s Rx data PDU segment of SN=%d (%d B), SO=%d, N_li=%d " ,
RB_NAME , header . sn , nof_bytes , header . so , header . N_li ) ;
log - > debug ( " %s \n " , rlc_amd_pdu_header_to_string ( header ) . c_str ( ) ) ;
// Check inside rx window
if ( ! inside_rx_window ( header . sn ) ) {
@ -1443,9 +1446,9 @@ void rlc_am::rlc_am_rx::handle_data_pdu_segment(uint8_t *payload, uint32_t nof_b
// Check if we already have a segment from the same PDU
it = rx_segments . find ( header . sn ) ;
if ( rx_segments . end ( ) ! = it ) {
if ( rx_segments . end ( ) ! = it ) {
if ( header . p ) {
if ( header . p ) {
log - > info ( " %s Status packet requested through polling bit \n " , RB_NAME ) ;
do_status = true ;
}
@ -1517,6 +1520,9 @@ void rlc_am::rlc_am_rx::reassemble_rx_sdus()
for ( uint32_t i = 0 ; i < rx_window [ vr_r ] . header . N_li ; i + + )
{
len = rx_window [ vr_r ] . header . li [ i ] ;
log - > debug_hex ( rx_window [ vr_r ] . buf - > msg , len , " Handling segment %d/%d of length %d B of SN=%d \n " , i + 1 , rx_window [ vr_r ] . header . N_li , len , vr_r ) ;
// sanity check to avoid zero-size SDUs
if ( len = = 0 ) {
break ;
@ -1556,11 +1562,12 @@ void rlc_am::rlc_am_rx::reassemble_rx_sdus()
// Handle last segment
len = rx_window [ vr_r ] . buf - > N_bytes ;
log - > debug_hex ( rx_window [ vr_r ] . buf - > msg , len , " Handling last segment of length %d B of SN=%d \n " , len , vr_r ) ;
if ( rx_sdu - > get_tailroom ( ) > = len ) {
memcpy ( & rx_sdu - > msg [ rx_sdu - > N_bytes ] , rx_window [ vr_r ] . buf - > msg , len ) ;
rx_sdu - > N_bytes + = rx_window [ vr_r ] . buf - > N_bytes ;
} else {
log - > error ( " Cannot fit RLC PDU in SDU buffer, dropping both. \n " ) ;
log - > error ( " Cannot fit RLC PDU in SDU buffer, dropping both. Erasing SN=%d. \n " , vr_r ) ;
pool - > deallocate ( rx_sdu ) ;
pool - > deallocate ( rx_window [ vr_r ] . buf ) ;
rx_window . erase ( vr_r ) ;
@ -1584,6 +1591,19 @@ void rlc_am::rlc_am_rx::reassemble_rx_sdus()
exit :
// Move the rx_window
log - > debug ( " Erasing SN=%d. \n " , vr_r ) ;
// also erase any segments of this SN
std : : map < uint32_t , rlc_amd_rx_pdu_segments_t > : : iterator it ;
it = rx_segments . find ( vr_r ) ;
if ( rx_segments . end ( ) ! = it ) {
log - > debug ( " Erasing segments of SN=%d \n " , vr_r ) ;
std : : list < rlc_amd_rx_pdu_t > : : iterator segit ;
for ( segit = it - > second . segments . begin ( ) ; segit ! = it - > second . segments . end ( ) ; + + segit ) {
log - > debug ( " Erasing segment of SN=%d SO=%d Len=%d N_li=%d \n " , segit - > header . sn , segit - > header . so , segit - > buf - > N_bytes , segit - > header . N_li ) ;
pool - > deallocate ( segit - > buf ) ;
}
it - > second . segments . clear ( ) ;
}
pool - > deallocate ( rx_window [ vr_r ] . buf ) ;
rx_window . erase ( vr_r ) ;
vr_r = ( vr_r + 1 ) % MOD ;
@ -1669,8 +1689,8 @@ void rlc_am::rlc_am_rx::timer_expired(uint32_t timeout_id)
pthread_mutex_unlock ( & mutex ) ;
}
// Called from Tx object (packs status PDU and returns length of it)
int rlc_am : : rlc_am_rx : : get_status ( rlc_status_pdu_t * status )
// Called from Tx object to pack status PDU that doesn't exceed a given size
int rlc_am : : rlc_am_rx : : get_status _pdu ( rlc_status_pdu_t * status , const uint32_t max_pdu_size )
{
pthread_mutex_lock ( & mutex ) ;
status - > N_nack = 0 ;
@ -1678,7 +1698,8 @@ int rlc_am::rlc_am_rx::get_status(rlc_status_pdu_t* status)
// We don't use segment NACKs - just NACK the full PDU
uint32_t i = vr_r ;
while ( RX_MOD_BASE ( i ) < RX_MOD_BASE ( vr_ms ) & & status - > N_nack < RLC_AM_WINDOW_SIZE ) {
while ( RX_MOD_BASE ( i ) < RX_MOD_BASE ( vr_ms ) & & status - > N_nack < RLC_AM_WINDOW_SIZE & & rlc_am_packed_length ( status ) < = max_pdu_size - 2 ) {
status - > ack_sn = i ;
if ( rx_window . find ( i ) = = rx_window . end ( ) ) {
status - > nacks [ status - > N_nack ] . nack_sn = i ;
status - > N_nack + + ;
@ -1689,6 +1710,22 @@ int rlc_am::rlc_am_rx::get_status(rlc_status_pdu_t* status)
return rlc_am_packed_length ( status ) ;
}
// Called from Tx object to obtain length of the full status PDU
int rlc_am : : rlc_am_rx : : get_status_pdu_length ( )
{
pthread_mutex_lock ( & mutex ) ;
rlc_status_pdu_t status ;
uint32_t i = vr_r ;
while ( RX_MOD_BASE ( i ) < RX_MOD_BASE ( vr_ms ) & & status . N_nack < RLC_AM_WINDOW_SIZE ) {
if ( rx_window . find ( i ) = = rx_window . end ( ) ) {
status . N_nack + + ;
}
i = ( i + 1 ) % MOD ;
}
pthread_mutex_unlock ( & mutex ) ;
return rlc_am_packed_length ( & status ) ;
}
void rlc_am : : rlc_am_rx : : print_rx_segments ( )
{
std : : map < uint32_t , rlc_amd_rx_pdu_segments_t > : : iterator it ;
@ -1757,33 +1794,46 @@ bool rlc_am::rlc_am_rx::add_segment_and_check(rlc_amd_rx_pdu_segments_t *pdu, rl
header . fi | = ( pdu - > segments . front ( ) . header . fi & RLC_FI_FIELD_NOT_START_ALIGNED ) ;
header . fi | = ( pdu - > segments . back ( ) . header . fi & RLC_FI_FIELD_NOT_END_ALIGNED ) ;
log - > debug ( " Starting header reconstruction of %zd segments \n " , pdu - > segments . size ( ) ) ;
// Reconstruct li fields
uint16_t count = 0 ;
uint16_t carryover = 0 ;
for ( it = pdu - > segments . begin ( ) ; it ! = pdu - > segments . end ( ) ; it + + ) {
if ( it - > header . N_li > 0 ) {
header . li [ header . N_li + + ] = it - > header . li [ 0 ] + carryover ;
count + = it - > header . li [ 0 ] ;
for ( uint32_t i = 1 ; i < it - > header . N_li ; i + + ) {
header . li [ header . N_li + + ] = it - > header . li [ i ] ;
log - > debug ( " Handling %d PDU segments \n " , it - > header . N_li ) ;
for ( uint32_t i = 0 ; i < it - > header . N_li ; i + + ) {
header . li [ header . N_li ] = it - > header . li [ i ] ;
if ( i = = 0 ) {
header . li [ header . N_li ] + = carryover ;
}
log - > debug ( " - adding segment %d/%d (%d B, SO=%d, carryover=%d, count=%d) \n " , i + 1 , it - > header . N_li , header . li [ header . N_li ] , header . so , carryover , count ) ;
header . N_li + + ;
count + = it - > header . li [ i ] ;
}
carryover = 0 ;
}
// accumulate segment sizes until end aligned PDU is received
if ( rlc_am_not_start_aligned ( it - > header . fi ) ) {
if ( count < = it - > buf - > N_bytes ) {
carryover + = it - > buf - > N_bytes - count ;
log - > debug ( " Incremented carryover (it->buf->N_bytes=%d, count=%d). New carryover=%d \n " , it - > buf - > N_bytes , count , carryover ) ;
} else {
carryover = it - > buf - > N_bytes - count ;
// Next segment would be too long, recalculate carryover
header . N_li - - ;
carryover = it - > buf - > N_bytes - ( count - header . li [ header . N_li ] ) ;
log - > debug ( " Recalculated carryover=%d (it->buf->N_bytes=%d, count=%d, header.li[header.N_li]=%d) \n " , carryover , it - > buf - > N_bytes , count , header . li [ header . N_li ] ) ;
}
tmpit = it ;
if ( rlc_am_end_aligned ( it - > header . fi ) & & + + tmpit ! = pdu - > segments . end ( ) ) {
header . li [ header . N_li + + ] = carryover ;
log - > debug ( " Header is end-aligned, overwrite header.li[%d]=%d \n " , header . N_li , carryover ) ;
header . li [ header . N_li ] = carryover ;
header . N_li + + ;
carryover = 0 ;
}
count = 0 ;
}
log - > debug ( " Finished header reconstruction of %zd segments \n " , pdu - > segments . size ( ) ) ;
// Copy data
byte_buffer_t * full_pdu = pool_allocate_blocking ;
if ( full_pdu = = NULL ) {
@ -2090,7 +2140,7 @@ bool rlc_am_is_pdu_segment(uint8_t *payload)
return ( ( * ( payload ) > > 6 ) & 0x01 ) = = 1 ;
}
std : : string rlc_am_ to_string( rlc_status_pdu_t * status )
std : : string rlc_am_ status_pdu_ to_string( rlc_status_pdu_t * status )
{
std : : stringstream ss ;
ss < < " ACK_SN = " < < status - > ack_sn ;
@ -2111,6 +2161,28 @@ std::string rlc_am_to_string(rlc_status_pdu_t *status)
return ss . str ( ) ;
}
std : : string rlc_amd_pdu_header_to_string ( const rlc_amd_pdu_header_t & header )
{
std : : stringstream ss ;
ss < < " [ " < < rlc_dc_field_text [ header . dc ] ;
ss < < " , RF= " < < ( header . rf ? " 1 " : " 0 " ) ;
ss < < " , P= " < < ( header . p ? " 1 " : " 0 " ) ;
ss < < " , FI= " < < ( header . fi ? " 1 " : " 0 " ) ;
ss < < " , SN= " < < header . sn ;
ss < < " , LSF= " < < ( header . lsf ? " 1 " : " 0 " ) ;
ss < < " , SO= " < < header . so ;
ss < < " , N_li= " < < header . N_li ;
if ( header . N_li > 0 ) {
ss < < " ( " ;
for ( uint32_t i = 0 ; i < header . N_li ; i + + ) {
ss < < header . li [ i ] < < " , " ;
}
ss < < " ) " ;
}
ss < < " ] " ;
return ss . str ( ) ;
}
bool rlc_am_start_aligned ( const uint8_t fi )
{
return ( fi = = RLC_FI_FIELD_START_AND_END_ALIGNED | | fi = = RLC_FI_FIELD_NOT_END_ALIGNED ) ;