Files
openairinterface5g/openair2/LAYER2/RLC/AM/rlc_am_segment.c
T
ghaddab c23783c702 License corrected
git-svn-id: http://svn.eurecom.fr/openair4G/trunk@5714 818b1a75-f10b-46b9-bf7c-635c3b92a50f
2014-08-14 07:26:42 +00:00

626 lines
29 KiB
C
Executable File

/*******************************************************************************
OpenAirInterface
Copyright(c) 1999 - 2014 Eurecom
OpenAirInterface is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OpenAirInterface 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with OpenAirInterface.The full GNU General Public License is
included in this distribution in the file called "COPYING". If not,
see <http://www.gnu.org/licenses/>.
Contact Information
OpenAirInterface Admin: openair_admin@eurecom.fr
OpenAirInterface Tech : openair_tech@eurecom.fr
OpenAirInterface Dev : openair4g-devel@eurecom.fr
Address : Eurecom, Campus SophiaTech, 450 Route des Chappes, CS 50193 - 06904 Biot Sophia Antipolis cedex, FRANCE
*******************************************************************************/
/***************************************************************************
rlc_am_segment.c -
-------------------
AUTHOR : Lionel GAUTHIER
COMPANY : EURECOM
EMAIL : Lionel.Gauthier@eurecom.fr
***************************************************************************/
#include "rtos_header.h"
//-----------------------------------------------------------------------------
#include "list.h"
#include "rlc_am_entity.h"
#include "rlc_am_structs.h"
#include "rlc_am_constants.h"
#include "rlc_primitives.h"
#include "mem_block.h"
//#define DEBUG_RLC_AM_SEGMENT
//#define DEBUG_RLC_AM_SEGMENT_FILL_DATA
//#define DEBUG_MEM_MNGT
//-----------------------------------------------------------------------------
mem_block_t *rlc_am_segment_15 (struct rlc_am_entity *rlcP);
mem_block_t *rlc_am_segment_7 (struct rlc_am_entity *rlcP);
//-----------------------------------------------------------------------------
inline void
rlc_am_encode_pdu_15 (struct rlc_am_pdu_header *rlc_headerP, struct rlc_am_tx_data_pdu_management *pdu_mngtP, uint16_t * li_arrayP, uint8_t nb_liP)
{
//-----------------------------------------------------------------------------
uint8_t li_index;
#ifdef DEBUG_RLC_AM_SEGMENT
int index;
msg ("[RLC_AM] SEGMENT_15 PDU SET LIs: ");
for (index = 0; index < nb_liP; index++) {
msg ("%04X ", li_arrayP[index]);
}
msg ("\n");
#endif
rlc_headerP->byte1 = RLC_DC_DATA_PDU;
rlc_headerP->byte2 = 0;
nb_liP = nb_liP << 1;
pdu_mngtP->payload = (uint8_t *) ((uint32_t) (&rlc_headerP->li_data_7[nb_liP]));
if (nb_liP) {
rlc_headerP->byte2 |= RLC_E_NEXT_FIELD_IS_LI_E;
li_index = 0;
// COPY LI
while (li_index < nb_liP) {
rlc_headerP->li_data_7[li_index] = li_arrayP[li_index >> 1] >> 8;
li_index += 1;
rlc_headerP->li_data_7[li_index] = li_arrayP[li_index >> 1] | RLC_E_NEXT_FIELD_IS_LI_E;
li_index += 1;
}
rlc_headerP->li_data_7[li_index - 1] = rlc_headerP->li_data_7[li_index - 1] ^ (uint8_t) RLC_E_NEXT_FIELD_IS_LI_E;
}
}
//-----------------------------------------------------------------------------
inline void
rlc_am_encode_pdu_7 (struct rlc_am_pdu_header *rlc_headerP, struct rlc_am_tx_data_pdu_management *pdu_mngtP, uint16_t * li_arrayP, uint8_t nb_liP)
{
//-----------------------------------------------------------------------------
uint8_t li_index;
#ifdef DEBUG_RLC_AM_SEGMENT
int index;
msg ("[RLC_AM] SEGMENT_7 PDU %p SET LIs: ", pdu_mngtP);
for (index = 0; index < nb_liP; index++) {
msg ("%04X ", li_arrayP[index]);
}
msg ("\n");
#endif
rlc_headerP->byte1 = RLC_DC_DATA_PDU;
rlc_headerP->byte2 = 0;
pdu_mngtP->payload = (uint8_t *) ((uint32_t) (&rlc_headerP->li_data_7[nb_liP]));
if (nb_liP) {
rlc_headerP->byte2 |= RLC_E_NEXT_FIELD_IS_LI_E;
li_index = 0;
// COPY LI
while (li_index < nb_liP) {
rlc_headerP->li_data_7[li_index] = (uint8_t) (li_arrayP[li_index]) | RLC_E_NEXT_FIELD_IS_LI_E;
li_index += 1;
}
rlc_headerP->li_data_7[li_index - 1] = rlc_headerP->li_data_7[li_index - 1] ^ (uint8_t) RLC_E_NEXT_FIELD_IS_LI_E;
}
}
//-----------------------------------------------------------------------------
inline void
rlc_am_fill_pdu (mem_block_t * pduP, list_t * segmented_sdusP)
{
//-----------------------------------------------------------------------------
mem_block_t *sdu;
uint8_t *data_sdu;
uint8_t *data_pdu;
struct rlc_am_pdu_header *rlc_header;
struct rlc_am_tx_data_pdu_management *pdu_mngt;
struct rlc_am_tx_sdu_management *sdu_mngt;
int continue_fill;
uint16_t pdu_remaining_size;
uint16_t sdu_available_size;
sdu = NULL;
pdu_mngt = (struct rlc_am_tx_data_pdu_management *) (pduP->data);
rlc_header = (struct rlc_am_pdu_header *) (pdu_mngt->first_byte);
pdu_remaining_size = pdu_mngt->data_size;
data_pdu = (uint8_t *) pdu_mngt->payload;
#ifdef DEBUG_RLC_AM_SEGMENT_FILL_DATA
msg ("[RLC_AM] SEGMENT FILL GET PDU %p REMAINING SIZE=%d\n", pduP, pdu_remaining_size);
#endif
continue_fill = 1;
while ((continue_fill)) {
// fill the pdu with data of sdu
if (sdu == NULL) {
sdu = list_remove_head (segmented_sdusP);
if (sdu == NULL) {
return; // case pdu only 2 LIs : exactly_filled or one_byte_short + paddind
}
sdu_mngt = (struct rlc_am_tx_sdu_management *) (sdu->data);
data_sdu = &sdu->data[sizeof (struct rlc_am_tx_sdu_management) + sdu_mngt->sdu_segmented_size];
sdu_available_size = sdu_mngt->sdu_size - sdu_mngt->sdu_remaining_size - sdu_mngt->sdu_segmented_size;
#ifdef DEBUG_RLC_AM_SEGMENT_FILL_DATA
msg ("[RLC_AM] SEGMENT FILL GET SDU %p AVAILABLE_SZ=%d (SZ %d REMAIN_SZ %d SEGMENT_SZ %d)\n",
sdu, sdu_available_size, sdu_mngt->sdu_size, sdu_mngt->sdu_remaining_size, sdu_mngt->sdu_segmented_size);
#endif
}
// copy the whole remaining data of the sdu in the remaining area of the pdu
if (pdu_remaining_size >= sdu_available_size) {
memcpy (data_pdu, data_sdu, sdu_available_size);
#ifdef DEBUG_RLC_AM_SEGMENT_FILL_DATA
msg ("[RLC_AM] SEGMENT FILL PDU %p WITH SDU %p %p <- %p %d bytes (SZ %d REMAIN_SZ %d SEGMENT_SZ %d)\n",
pduP, sdu, data_pdu, data_sdu, sdu_available_size, sdu_mngt->sdu_size, sdu_mngt->sdu_remaining_size, sdu_mngt->sdu_segmented_size);
#endif
pdu_remaining_size -= sdu_available_size;
sdu_mngt->sdu_segmented_size += sdu_available_size;
data_pdu = (uint8_t *) ((uint32_t) data_pdu + sdu_available_size);
sdu_available_size = 0;
// dispatch the sdu
sdu = NULL;
// dispatch the pdu
if (pdu_remaining_size == 0) {
continue_fill = 0;
} else {
}
// copy some data of the sdu in the whole remaining area of the pdu
} else {
memcpy (data_pdu, data_sdu, pdu_remaining_size);
#ifdef DEBUG_RLC_AM_SEGMENT_FILL_DATA
msg ("[RLC_AM] SEGMENT FILL PDU %p WITH SDU %p %p <- %p %d bytes (SZ %d REMAIN_SZ %d SEGMENT_SZ %d)\n",
pduP, sdu, data_pdu, data_sdu, pdu_remaining_size, sdu_mngt->sdu_size, sdu_mngt->sdu_remaining_size, sdu_mngt->sdu_segmented_size);
#endif
sdu_mngt->sdu_segmented_size += pdu_remaining_size;
sdu_available_size -= pdu_remaining_size;
data_sdu = (uint8_t *) ((uint32_t) data_sdu + (uint32_t) pdu_remaining_size);
continue_fill = 0;
pdu_remaining_size = 0;
}
}
list_free (segmented_sdusP);
}
//-----------------------------------------------------------------------------
mem_block_t *
rlc_am_segment_15 (struct rlc_am_entity *rlcP)
{
//-----------------------------------------------------------------------------
list_t segmented_sdus; // the copying of sdu data is done after identification of all LIs to put in pdu
struct rlc_am_tx_sdu_management *sdu_mngt;
struct rlc_am_tx_data_pdu_management *pdu_mngt;
struct rlc_am_pdu_header *rlc_header;
mem_block_t *pdu;
int continue_segment = 1;
int16_t pdu_remaining_size;
uint8_t li_index = 0;
pdu = NULL;
list_init (&segmented_sdus, NULL); // param string identifying the list is NULL
while ((rlcP->input_sdus[rlcP->current_sdu_index]) && (continue_segment)) {
sdu_mngt = ((struct rlc_am_tx_sdu_management *) (rlcP->input_sdus[rlcP->current_sdu_index]->data));
if (!(sdu_mngt->segmented)) {
// pdu management
if (!(pdu)) {
if (!(pdu = get_free_mem_block (rlcP->pdu_size + sizeof (struct rlc_am_tx_data_pdu_allocation) + GUARD_CRC_LIH_SIZE))) {
#ifdef DEBUG_MEM_MNGT
msg ("[RLC_AM][RB %d][SEGMENT] ERROR COULD NOT GET NEW PDU, EXIT\n", rlcP->rb_id);
#endif
return NULL;
}
li_index = 0;
pdu_remaining_size = rlcP->pdu_size - 2; // 2= size of header, minimum
pdu_mngt = (struct rlc_am_tx_data_pdu_management *) (pdu->data);
memset (pdu->data, 0, sizeof (struct rlc_am_tx_data_pdu_allocation));
pdu_mngt->rlc_tb_type = RLC_AM_DATA_PDU_TYPE;
pdu_mngt->first_byte = &pdu->data[sizeof (struct rlc_am_tx_data_pdu_allocation)];
rlc_header = (struct rlc_am_pdu_header *) (pdu_mngt->first_byte);
}
if (sdu_mngt->sdu_remaining_size > 0) { // think RLC_LI_LAST_PDU_ONE_BYTE_SHORT, RLC_LI_LAST_PDU_EXACTLY_FILLED
if ((pdu_remaining_size - sdu_mngt->sdu_remaining_size) < 0) {
pdu_mngt->data_size += pdu_remaining_size;
rlc_am_encode_pdu_15 (rlc_header, pdu_mngt, rlcP->li, li_index);
continue_segment = 0;
sdu_mngt->sdu_remaining_size = sdu_mngt->sdu_remaining_size - pdu_remaining_size;
sdu_mngt->nb_pdus += 1;
pdu_mngt->sdu[pdu_mngt->nb_sdu++] = rlcP->current_sdu_index;
} else {
// From 3GPP TS 25.322 V4.2.0 (2001-09) p28
// If "SDU discard with explicit signalling" is configured:
// - an AMD PDU can contain a maximum number of 15 "Length Indicators" indicating the end
// of 15 corresponding SDUs; and
// - the rest of the AMD PDU space shall be used as padding or as piggybacked STATUS PDU.
// implementation limitation : we limit the number of li to 15 even if
// no "SDU discard with explicit signalling" is configured.
if (li_index >= 14) {
rlcP->li[li_index++] = RLC_LI_PDU_PADDING;
rlc_am_encode_pdu_15 (rlc_header, pdu_mngt, rlcP->li, li_index);
continue_segment = 0;
sdu_mngt->no_new_sdu_segmented_in_last_pdu = 1;
} else { // nb li have not reached the limit
if ((pdu_remaining_size - sdu_mngt->sdu_remaining_size) >= 2) {
pdu_remaining_size = pdu_remaining_size - sdu_mngt->sdu_remaining_size - 2; // size of li length
pdu_mngt->data_size += sdu_mngt->sdu_remaining_size;
sdu_mngt->li_index_for_discard = li_index;
rlcP->li[li_index++] = sdu_mngt->sdu_remaining_size << 1;
sdu_mngt->sdu_remaining_size = 0;
sdu_mngt->segmented = 1;
sdu_mngt->nb_pdus += 1;
pdu_mngt->sdu[pdu_mngt->nb_sdu++] = rlcP->current_sdu_index;
pdu_mngt->last_pdu_of_sdu += 1;
list_add_tail_eurecom (rlcP->input_sdus[rlcP->current_sdu_index], &segmented_sdus);
rlcP->buffer_occupancy -= sdu_mngt->sdu_size;
rlcP->nb_sdu -= 1;
rlcP->current_sdu_index = (rlcP->current_sdu_index + 1) % rlcP->size_input_sdus_buffer;
//} else the while loop continue with the same pdu
} else if ((pdu_remaining_size - sdu_mngt->sdu_remaining_size) == 0) {
// from 3GPP TS 25.322 V4.2.0
//In the case where the end of the last segment of an RLC SDU exactly ends at the end of
// a PDU and there is no "Length Indicator" that indicates the end of the RLC SDU:
// - if 7-bit "Length Indicator" is used:
// - a "Length Indicator" with value "000 0000" shall be placed as the first "Length
// Indicator" in the following PDU;
// - if 15-bit "Length Indicator" is used:
// - a "Length Indicator" with value "000 0000 0000 0000" shall be placed as the first
// "Length Indicator" in the following PDU.
pdu_mngt->data_size += pdu_remaining_size;
rlc_am_encode_pdu_15 (rlc_header, pdu_mngt, rlcP->li, li_index);
continue_segment = 0;
sdu_mngt->sdu_remaining_size = 0;
sdu_mngt->nb_pdus += 1;
pdu_mngt->sdu[pdu_mngt->nb_sdu++] = rlcP->current_sdu_index;
list_add_tail_eurecom (rlcP->input_sdus[rlcP->current_sdu_index], &segmented_sdus);
rlcP->li_exactly_filled_to_add_in_next_pdu = 1;
} else if ((pdu_remaining_size - sdu_mngt->sdu_remaining_size) == 1) { // one byte remaining
// from 3GPP TS 25.322 V4.2.0
// In the case where a PDU contains a 15-bit "Length Indicator" indicating that an RLC SDU ends with
// one octet left in the PDU, the last octet of this PDU shall:
// - be padded by the Sender and ignored by the Receiver though there is no "Length Indicator"
// indicating the existence of Padding; and
// - not be filled with the first octet of the next RLC SDU data.
// In the case where 15-bit "Length Indicators" are used for the previous PDU and
// the last segment of an RLC SDU is one octet short of exactly filling the PDU:
// - if a 15-bit "Length Indicator" is used for the following PDU:
// - the "Length Indicator" with value "111 1111 1111 1011" shall be placed as
// the first "Length Indicator" in the following PDU;
// - the remaining one octet in the previous PDU shall be padded by the Sender
// and ignored at the Receiver though there is no "Length Indicator" indicating
// the existence of Padding;
// - in case this SDU was the last one to be transmitted:
// - a RLC PDU consisting of an RLC Header with "Length Indicator"
// "111 1111 1111 1011" followed by a padding "Length Indicator" and padding
// may be transmitted;
pdu_mngt->data_size += pdu_remaining_size;
rlc_am_encode_pdu_15 (rlc_header, pdu_mngt, rlcP->li, li_index);
continue_segment = 0;
sdu_mngt->sdu_remaining_size = 0;
sdu_mngt->nb_pdus += 1;
pdu_mngt->sdu[pdu_mngt->nb_sdu++] = rlcP->current_sdu_index;
list_add_tail_eurecom (rlcP->input_sdus[rlcP->current_sdu_index], &segmented_sdus);
rlcP->li_one_byte_short_to_add_in_next_pdu = 1;
}
}
}
// management of previous pdu
// In the case where 15-bit "Length Indicators" are used for the previous PDU and
// the last segmentof an RLC SDU is one octet short of exactly filling the PDU:
// - if a 15-bit "Length Indicator" is used for the following PDU:
// - the "Length Indicator" with value "111 1111 1111 1011" shall be placed
// as the first "Length Indicator" in the following PDU;
// - the remaining one octet in the previous PDU shall be padded by the Sender
// and ignored at the Receiver though there is no "Length Indicator" indicating
// the existence of Padding;
// - in case this SDU was the last one to be transmitted:
// - a RLC PDU consisting of an RLC Header with "Length Indicator"
// "111 1111 1111 1011" followed by a padding "Length Indicator" and padding
// may be transmitted;
} else if ((rlcP->li_one_byte_short_to_add_in_next_pdu)) {
sdu_mngt->li_index_for_discard = li_index;
rlcP->li[li_index++] = RLC_LI_LAST_PDU_ONE_BYTE_SHORT;
rlcP->li_one_byte_short_to_add_in_next_pdu = 0;
sdu_mngt->segmented = 1;
pdu_remaining_size -= 2;
pdu_mngt->last_pdu_of_sdu += 1;
pdu_mngt->sdu[pdu_mngt->nb_sdu++] = rlcP->current_sdu_index;
sdu_mngt->nb_pdus += 1;
rlcP->buffer_occupancy -= sdu_mngt->sdu_size;
rlcP->nb_sdu -= 1;
rlcP->current_sdu_index = (rlcP->current_sdu_index + 1) % rlcP->size_input_sdus_buffer;
} else if ((rlcP->li_exactly_filled_to_add_in_next_pdu)) {
sdu_mngt->li_index_for_discard = li_index;
rlcP->li[li_index++] = RLC_LI_LAST_PDU_EXACTLY_FILLED;
rlcP->li_exactly_filled_to_add_in_next_pdu = 0;
sdu_mngt->segmented = 1;
pdu_remaining_size -= 2;
pdu_mngt->last_pdu_of_sdu += 1;
pdu_mngt->sdu[pdu_mngt->nb_sdu++] = rlcP->current_sdu_index;
sdu_mngt->nb_pdus += 1;
rlcP->buffer_occupancy -= sdu_mngt->sdu_size;
rlcP->nb_sdu -= 1;
rlcP->current_sdu_index = (rlcP->current_sdu_index + 1) % rlcP->size_input_sdus_buffer;
}
}
if (sdu_mngt->sdu_remaining_size > 0) {
list_add_tail_eurecom (rlcP->input_sdus[rlcP->current_sdu_index], &segmented_sdus);
}
}
if ((pdu)) {
if ((continue_segment > 0)) { // means pdu not totaly filled
if (pdu_remaining_size >= 2) {
rlcP->li[li_index++] = RLC_LI_PDU_PADDING;
sdu_mngt->no_new_sdu_segmented_in_last_pdu = 1;
} else if (pdu_remaining_size == 0) {
rlcP->li_exactly_filled_to_add_in_next_pdu = 1;
} else if (pdu_remaining_size == 1) {
rlcP->li_one_byte_short_to_add_in_next_pdu = 1;
}
rlc_am_encode_pdu_15 (rlc_header, pdu_mngt, rlcP->li, li_index);
}
rlc_am_fill_pdu (pdu, &segmented_sdus);
} else {
// patch for max dat discard : avoid waiting sdu pointed by current_sdu_index <> NULL
// (this sdu may have been segmented not at all then discarded)
while ((rlcP->next_sdu_index != rlcP->current_sdu_index) && (rlcP->input_sdus[rlcP->current_sdu_index] == NULL)) {
rlcP->current_sdu_index = (rlcP->current_sdu_index + 1) % rlcP->size_input_sdus_buffer;
rlcP->nb_sdu -= 1;
}
}
return pdu;
}
//-----------------------------------------------------------------------------
mem_block_t *
rlc_am_segment_7 (struct rlc_am_entity * rlcP)
{
//-----------------------------------------------------------------------------
list_t segmented_sdus; // the copying of sdu data is done after identification of all LIs to put in pdu
struct rlc_am_tx_sdu_management *sdu_mngt = NULL;
struct rlc_am_tx_data_pdu_management *pdu_mngt;
struct rlc_am_pdu_header *rlc_header;
mem_block_t *pdu = NULL;
int continue_segment = 1;
int16_t pdu_remaining_size;
uint8_t li_index = 0;
list_init (&segmented_sdus, NULL); // param string identifying the list is NULL
while ((rlcP->input_sdus[rlcP->current_sdu_index]) && (continue_segment)) {
sdu_mngt = ((struct rlc_am_tx_sdu_management *) (rlcP->input_sdus[rlcP->current_sdu_index]->data));
if (!(sdu_mngt->segmented)) {
#ifdef DEBUG_RLC_AM_SEGMENT
msg ("[RLC_AM][RB %d] SEGMENT GET NEW SDU %p INDEX %d AVAILABLE SIZE %d Bytes\n", rlcP->rb_id, sdu_mngt, rlcP->current_sdu_index, sdu_mngt->sdu_remaining_size);
#endif
if (!(pdu)) {
// pdu management
if (!(pdu = get_free_mem_block (rlcP->pdu_size + sizeof (struct rlc_am_tx_data_pdu_allocation) + GUARD_CRC_LIH_SIZE))) {
#ifdef DEBUG_MEM_MNGT
msg ("[RLC_AM][RB %d][SEGMENT7] ERROR COULD NOT GET NEW PDU, EXIT\n", rlcP->rb_id);
#endif
return NULL;
}
li_index = 0;
pdu_remaining_size = rlcP->pdu_size - 2; // 2= size of header, minimum
pdu_mngt = (struct rlc_am_tx_data_pdu_management *) (pdu->data);
memset (pdu->data, 0, sizeof (struct rlc_am_tx_data_pdu_allocation));
pdu_mngt->rlc_tb_type = RLC_AM_DATA_PDU_TYPE;
pdu_mngt->first_byte = &pdu->data[sizeof (struct rlc_am_tx_data_pdu_allocation)];
rlc_header = (struct rlc_am_pdu_header *) (pdu_mngt->first_byte);
if ((rlcP->li_exactly_filled_to_add_in_next_pdu)) {
pdu_remaining_size -= 1;
}
#ifdef DEBUG_RLC_AM_SEGMENT
msg ("[RLC_AM][RB %d][SEGMENT7] CONSTRUCT NEW PDU %p %p\n", rlcP->rb_id, pdu, pdu_mngt);
#endif
}
if (sdu_mngt->sdu_remaining_size > 0) {
msg("sdu_mngt->sdu_remaining_size %d\n",sdu_mngt->sdu_remaining_size);
if ((pdu_remaining_size - sdu_mngt->sdu_remaining_size) < 0) {
pdu_mngt->data_size += pdu_remaining_size;
rlcP->buffer_occupancy -= pdu_remaining_size;
sdu_mngt->nb_pdus += 1;
pdu_mngt->sdu[pdu_mngt->nb_sdu++] = rlcP->current_sdu_index;
rlc_am_encode_pdu_7 (rlc_header, pdu_mngt, rlcP->li, li_index);
continue_segment = 0;
sdu_mngt->sdu_remaining_size = sdu_mngt->sdu_remaining_size - pdu_remaining_size;
pdu_remaining_size = 0;
#ifdef DEBUG_RLC_AM_SEGMENT
msg ("[RLC_AM][RB %d][SEGMENT7] PDU %p %p FILLED WITH SDU index %d SDU REMAINING %d\n", rlcP->rb_id, pdu, pdu_mngt, rlcP->current_sdu_index, sdu_mngt->sdu_remaining_size);
#endif
} else {
// From 3GPP TS 25.322 V4.2.0 (2001-09) p28
// If "SDU discard with explicit signalling" is configured:
// - an AMD PDU can contain a maximum number of 15 "Length Indicators" indicating the end
// of 15 corresponding SDUs; and
// - the rest of the AMD PDU space shall be used as padding or as piggybacked STATUS PDU.
// implementation limitation : we limit the number of li to 15 even if
// no "SDU discard with explicit signalling" is configured.
if (li_index >= 14) {
rlcP->li[li_index++] = RLC_LI_PDU_PADDING;
rlc_am_encode_pdu_7 (rlc_header, pdu_mngt, rlcP->li, li_index);
continue_segment = 0;
sdu_mngt->no_new_sdu_segmented_in_last_pdu = 1;
#ifdef DEBUG_RLC_AM_SEGMENT
msg ("[RLC_AM][RB %d][SEGMENT7] NB MAX LI REACHED\n", rlcP->rb_id);
#endif
} else { // nb li have not reached the limit
if ((pdu_remaining_size - sdu_mngt->sdu_remaining_size) >= 1) {
pdu_remaining_size = pdu_remaining_size - sdu_mngt->sdu_remaining_size - 1; // size of li length
pdu_mngt->data_size += sdu_mngt->sdu_remaining_size;
sdu_mngt->li_index_for_discard = li_index;
rlcP->li[li_index++] = sdu_mngt->sdu_remaining_size << 1;
rlcP->buffer_occupancy -= sdu_mngt->sdu_remaining_size;
sdu_mngt->sdu_remaining_size = 0;
sdu_mngt->segmented = 1;
sdu_mngt->nb_pdus += 1;
pdu_mngt->sdu[pdu_mngt->nb_sdu++] = rlcP->current_sdu_index;
pdu_mngt->last_pdu_of_sdu += 1;
list_add_tail_eurecom (rlcP->input_sdus[rlcP->current_sdu_index], &segmented_sdus);
rlcP->nb_sdu -= 1;
#ifdef DEBUG_RLC_AM_SEGMENT
msg ("[RLC_AM][RB %d][SEGMENT7] SDU index %d->%d ENDING IN PDU %p %p PDU REMAINING SIZE %d\n", rlcP->rb_id,
rlcP->current_sdu_index, (rlcP->current_sdu_index + 1) % rlcP->size_input_sdus_buffer, pdu, pdu_mngt, pdu_remaining_size);
#endif
rlcP->current_sdu_index = (rlcP->current_sdu_index + 1) % rlcP->size_input_sdus_buffer;
// case where Li length exactly ends the pdu
if (pdu_remaining_size == 0) {
continue_segment = 0;
rlc_am_encode_pdu_7 (rlc_header, pdu_mngt, rlcP->li, li_index);
}
/*
} else { // else:if (!last_sdu)
} // the while loop continue with the same pdu */
} else if ((pdu_remaining_size - sdu_mngt->sdu_remaining_size) == 0) {
// from 3GPP TS 25.322 V4.2.0
//In the case where the end of the last segment of an RLC SDU exactly ends at the end of
// a PDU and there is no "Length Indicator" that indicates the end of the RLC SDU:
// - if 7-bit "Length Indicator" is used:
// - a "Length Indicator" with value "000 0000" shall be placed as the first "Length
// Indicator" in the following PDU;
// - if 15-bit "Length Indicator" is used:
// - a "Length Indicator" with value "000 0000 0000 0000" shall be placed as the first
// "Length Indicator" in the following PDU.
pdu_mngt->data_size += pdu_remaining_size;
rlc_am_encode_pdu_7 (rlc_header, pdu_mngt, rlcP->li, li_index);
continue_segment = 0;
rlcP->buffer_occupancy -= sdu_mngt->sdu_remaining_size;
sdu_mngt->sdu_remaining_size = 0;
pdu_remaining_size = 0;
sdu_mngt->nb_pdus += 1;
pdu_mngt->sdu[pdu_mngt->nb_sdu++] = rlcP->current_sdu_index;
list_add_tail_eurecom (rlcP->input_sdus[rlcP->current_sdu_index], &segmented_sdus);
rlcP->li_exactly_filled_to_add_in_next_pdu = 1;
#ifdef DEBUG_RLC_AM_SEGMENT
msg ("[RLC_AM][RB %d][SEGMENT7] SDU index %d EXACTLY ENDING IN PDU %p %p\n", rlcP->rb_id, rlcP->current_sdu_index, pdu, pdu_mngt);
#endif
}
}
}
// management of previous pdu
// In the case where 15-bit "Length Indicators" are used for the previous PDU and the
// last segment of an RLC SDU is one octet short of exactly filling the PDU:
// - if a 15-bit "Length Indicator" is used for the following PDU:
// - the "Length Indicator" with value "111 1111 1111 1011" shall be placed as
// the first "Length Indicator" in the following PDU;
// - the remaining one octet in the previous PDU shall be padded by the Sender and
// ignored at the Receiver
// though there is no "Length Indicator" indicating the existence of Padding;
// - in case this SDU was the last one to be transmitted:
// - a RLC PDU consisting of an RLC Header with "Length Indicator"
// "111 1111 1111 1011" followed by a padding "Length Indicator" and padding
// may be transmitted;
// - if a 7-bit "Length Indicator" is used for the following PDU:
// - if RLC is configured for UM mode:
// - the "Length Indicator" with value "000 0000" shall be placed as the first
// "Length indicator" in the following PDU and its SN shall be incremented by 2
// before it is transmitted.
} else if ((rlcP->li_exactly_filled_to_add_in_next_pdu)) {
sdu_mngt->li_index_for_discard = li_index;
rlcP->li[li_index++] = RLC_LI_LAST_PDU_EXACTLY_FILLED;
rlcP->li_exactly_filled_to_add_in_next_pdu = 0;
sdu_mngt->segmented = 1;
//pdu_remaining_size -= 1;
pdu_mngt->last_pdu_of_sdu += 1;
pdu_mngt->sdu[pdu_mngt->nb_sdu++] = rlcP->current_sdu_index;
sdu_mngt->nb_pdus += 1;
//rlcP->buffer_occupancy -= sdu_mngt->sdu_size;
rlcP->nb_sdu -= 1;
#ifdef DEBUG_RLC_AM_SEGMENT
msg ("[RLC_AM][RB %d][SEGMENT7] RLC_LI_LAST_PDU_EXACTLY_FILLED -> PDU %p, %p, sdu index %d->%d \n", rlcP->rb_id, pdu, pdu_mngt,
rlcP->current_sdu_index, (rlcP->current_sdu_index + 1) % rlcP->size_input_sdus_buffer);
#endif
rlcP->current_sdu_index = (rlcP->current_sdu_index + 1) % rlcP->size_input_sdus_buffer;
}
}
if (sdu_mngt->sdu_remaining_size > 0) {
list_add_tail_eurecom (rlcP->input_sdus[rlcP->current_sdu_index], &segmented_sdus);
}
}
if ((pdu)) {
if ((continue_segment > 0)) { // means pdu not totaly filled
if (pdu_remaining_size >= 1) {
rlcP->li[li_index++] = RLC_LI_PDU_PADDING;
sdu_mngt->no_new_sdu_segmented_in_last_pdu = 1;
} else {
rlcP->li_exactly_filled_to_add_in_next_pdu = 1;
}
rlc_am_encode_pdu_7 (rlc_header, pdu_mngt, rlcP->li, li_index);
}
rlc_am_fill_pdu (pdu, &segmented_sdus);
} else {
// patch for max dat discard : avoid waiting sdu pointed by current_sdu_index <> NULL
// (this sdu may have been segmented not at all then discarded)
while ((rlcP->next_sdu_index != rlcP->current_sdu_index) && (rlcP->input_sdus[rlcP->current_sdu_index] == NULL)) {
rlcP->current_sdu_index = (rlcP->current_sdu_index + 1) % rlcP->size_input_sdus_buffer;
rlcP->nb_sdu -= 1;
}
}
return pdu;
}