Commit f147fa6e authored by Florian Kaltenberger's avatar Florian Kaltenberger

Merge remote-tracking branch 'origin/nr-rlc' into integration-develop-nr-2019w50

parents d046545b d2af44b7
......@@ -1473,15 +1473,8 @@ set(RRC_DIR ${OPENAIR2_DIR}/RRC/LTE)
set(NR_RRC_DIR ${OPENAIR2_DIR}/RRC/NR)
set(NR_UE_RRC_DIR ${OPENAIR2_DIR}/RRC/NR_UE)
set(PDCP_DIR ${OPENAIR2_DIR}/LAYER2/PDCP_v10.1.0)
set(L2_SRC
${OPENAIR2_DIR}/LAYER2/openair2_proc.c
${PDCP_DIR}/pdcp.c
${PDCP_DIR}/pdcp_fifo.c
${PDCP_DIR}/pdcp_sequence_manager.c
${PDCP_DIR}/pdcp_primitives.c
${PDCP_DIR}/pdcp_util.c
${PDCP_DIR}/pdcp_security.c
${PDCP_DIR}/pdcp_netlink.c
set(LTE_RLC_SRC
${RLC_AM_DIR}/rlc_am.c
${RLC_AM_DIR}/rlc_am_init.c
${RLC_AM_DIR}/rlc_am_timer_poll_retransmit.c
......@@ -1509,6 +1502,29 @@ set(L2_SRC
${RLC_DIR}/rlc.c
${RLC_DIR}/rlc_rrc.c
${RLC_DIR}/rlc_mpls.c
)
set(NR_RLC_SRC
${OPENAIR2_DIR}/LAYER2/nr_rlc/asn1_utils.c
${OPENAIR2_DIR}/LAYER2/nr_rlc/nr_rlc_entity.c
${OPENAIR2_DIR}/LAYER2/nr_rlc/nr_rlc_entity_am.c
${OPENAIR2_DIR}/LAYER2/nr_rlc/nr_rlc_entity_tm.c
${OPENAIR2_DIR}/LAYER2/nr_rlc/nr_rlc_entity_um.c
${OPENAIR2_DIR}/LAYER2/nr_rlc/nr_rlc_oai_api.c
${OPENAIR2_DIR}/LAYER2/nr_rlc/nr_rlc_pdu.c
${OPENAIR2_DIR}/LAYER2/nr_rlc/nr_rlc_sdu.c
${OPENAIR2_DIR}/LAYER2/nr_rlc/nr_rlc_ue_manager.c
)
set(L2_SRC
${OPENAIR2_DIR}/LAYER2/openair2_proc.c
${PDCP_DIR}/pdcp.c
${PDCP_DIR}/pdcp_fifo.c
${PDCP_DIR}/pdcp_sequence_manager.c
${PDCP_DIR}/pdcp_primitives.c
${PDCP_DIR}/pdcp_util.c
${PDCP_DIR}/pdcp_security.c
${PDCP_DIR}/pdcp_netlink.c
# ${RRC_DIR}/rrc_UE.c
${RRC_DIR}/rrc_eNB.c
${RRC_DIR}/rrc_eNB_S1AP.c
......@@ -1519,7 +1535,12 @@ set(L2_SRC
${RRC_DIR}/L2_interface_ue.c
)
set(L2_LTE_SRC
${LTE_RLC_SRC}
)
set(L2_NR_SRC
${NR_RLC_SRC}
${NR_RRC_DIR}/rrc_gNB.c
${NR_RRC_DIR}/nr_rrc_common.c
${NR_RRC_DIR}/L2_nr_interface.c
......@@ -1534,40 +1555,13 @@ set(L2_SRC_UE
${PDCP_DIR}/pdcp_util.c
${PDCP_DIR}/pdcp_security.c
${PDCP_DIR}/pdcp_netlink.c
${RLC_AM_DIR}/rlc_am.c
${RLC_AM_DIR}/rlc_am_init.c
${RLC_AM_DIR}/rlc_am_timer_poll_retransmit.c
${RLC_AM_DIR}/rlc_am_timer_reordering.c
${RLC_AM_DIR}/rlc_am_timer_status_prohibit.c
${RLC_AM_DIR}/rlc_am_segment.c
${RLC_AM_DIR}/rlc_am_segments_holes.c
${RLC_AM_DIR}/rlc_am_in_sdu.c
${RLC_AM_DIR}/rlc_am_receiver.c
${RLC_AM_DIR}/rlc_am_retransmit.c
${RLC_AM_DIR}/rlc_am_windows.c
${RLC_AM_DIR}/rlc_am_rx_list.c
${RLC_AM_DIR}/rlc_am_reassembly.c
${RLC_AM_DIR}/rlc_am_status_report.c
${RLC_TM_DIR}/rlc_tm.c
${RLC_TM_DIR}/rlc_tm_init.c
${RLC_UM_DIR}/rlc_um.c
${RLC_UM_DIR}/rlc_um_fsm.c
${RLC_UM_DIR}/rlc_um_control_primitives.c
${RLC_UM_DIR}/rlc_um_segment.c
${RLC_UM_DIR}/rlc_um_reassembly.c
${RLC_UM_DIR}/rlc_um_receiver.c
${RLC_UM_DIR}/rlc_um_dar.c
${RLC_DIR}/rlc_mac.c
${RLC_DIR}/rlc.c
${RLC_DIR}/rlc_rrc.c
${RLC_DIR}/rlc_mpls.c
${RRC_DIR}/rrc_UE.c
${RRC_DIR}/rrc_common.c
${RRC_DIR}/L2_interface_common.c
${RRC_DIR}/L2_interface_ue.c
)
set(LTE_NR_L2_SRC_UE
set(LTE_NR_L2_SRC_UE
${PDCP_DIR}/pdcp.c
${PDCP_DIR}/pdcp_fifo.c
${PDCP_DIR}/pdcp_sequence_manager.c
......@@ -1575,37 +1569,11 @@ set(L2_SRC_UE
${PDCP_DIR}/pdcp_util.c
${PDCP_DIR}/pdcp_security.c
${PDCP_DIR}/pdcp_netlink.c
${RLC_AM_DIR}/rlc_am.c
${RLC_AM_DIR}/rlc_am_init.c
${RLC_AM_DIR}/rlc_am_timer_poll_retransmit.c
${RLC_AM_DIR}/rlc_am_timer_reordering.c
${RLC_AM_DIR}/rlc_am_timer_status_prohibit.c
${RLC_AM_DIR}/rlc_am_segment.c
${RLC_AM_DIR}/rlc_am_segments_holes.c
${RLC_AM_DIR}/rlc_am_in_sdu.c
${RLC_AM_DIR}/rlc_am_receiver.c
${RLC_AM_DIR}/rlc_am_retransmit.c
${RLC_AM_DIR}/rlc_am_windows.c
${RLC_AM_DIR}/rlc_am_rx_list.c
${RLC_AM_DIR}/rlc_am_reassembly.c
${RLC_AM_DIR}/rlc_am_status_report.c
${RLC_TM_DIR}/rlc_tm.c
${RLC_TM_DIR}/rlc_tm_init.c
${RLC_UM_DIR}/rlc_um.c
${RLC_UM_DIR}/rlc_um_fsm.c
${RLC_UM_DIR}/rlc_um_control_primitives.c
${RLC_UM_DIR}/rlc_um_segment.c
${RLC_UM_DIR}/rlc_um_reassembly.c
${RLC_UM_DIR}/rlc_um_receiver.c
${RLC_UM_DIR}/rlc_um_dar.c
${RLC_DIR}/rlc_mac.c
${RLC_DIR}/rlc.c
${RLC_DIR}/rlc_rrc.c
${RLC_DIR}/rlc_mpls.c
${RRC_DIR}/L2_interface_common.c
${LTE_RLC_SRC}
)
set(NR_L2_SRC_UE
${NR_RLC_SRC}
${NR_UE_RRC_DIR}/L2_interface_ue.c
${NR_UE_RRC_DIR}/main_ue.c
${NR_UE_RRC_DIR}/rrc_UE.c
......@@ -1693,6 +1661,10 @@ add_library(MAC_UE_NR
${MAC_NR_SRC_UE}
)
add_library(L2_LTE
${L2_LTE_SRC}
)
add_library(L2_NR
${L2_NR_SRC}
${MAC_NR_SRC}
......@@ -2345,7 +2317,7 @@ add_dependencies(lte-softmodem rrc_flag s1ap_flag x2_flag)
target_link_libraries (lte-softmodem
-Wl,--start-group
RRC_LIB S1AP_LIB S1AP_ENB F1AP_LIB F1AP X2AP_LIB X2AP_ENB GTPV1U SECU_CN SECU_OSA UTIL HASHTABLE SCTP_CLIENT UDP SCHED_LIB SCHED_RU_LIB
PHY_COMMON PHY PHY_RU LFDS L2 NFAPI_COMMON_LIB NFAPI_LIB NFAPI_VNF_LIB NFAPI_PNF_LIB NFAPI_USER_LIB LFDS7
PHY_COMMON PHY PHY_RU LFDS L2 L2_LTE NFAPI_COMMON_LIB NFAPI_LIB NFAPI_VNF_LIB NFAPI_PNF_LIB NFAPI_USER_LIB LFDS7
${MSC_LIB} ${RAL_LIB} ${NAS_UE_LIB} ${ITTI_LIB} ${FLPT_MSG_LIB} ${ASYNC_IF_LIB} ${FLEXRAN_AGENT_LIB} ${FSPT_MSG_LIB} ${PROTO_AGENT_LIB}
-Wl,--end-group z dl)
......@@ -2417,7 +2389,7 @@ endif()
target_link_libraries (lte-uesoftmodem
-Wl,--start-group
RRC_LIB S1AP_LIB S1AP_ENB X2AP_LIB X2AP_ENB F1AP F1AP_LIB GTPV1U SECU_CN SECU_OSA UTIL HASHTABLE SCTP_CLIENT UDP SCHED_RU_LIB SCHED_UE_LIB PHY_COMMON
PHY_UE PHY_RU LFDS L2_UE SIMU ${MSC_LIB} ${RAL_LIB} ${NAS_UE_LIB} ${ITTI_LIB} ${FLPT_MSG_LIB} ${ASYNC_IF_LIB} LFDS7 ${ATLAS_LIBRARIES}
PHY_UE PHY_RU LFDS L2_UE L2_LTE SIMU ${MSC_LIB} ${RAL_LIB} ${NAS_UE_LIB} ${ITTI_LIB} ${FLPT_MSG_LIB} ${ASYNC_IF_LIB} LFDS7 ${ATLAS_LIBRARIES}
NFAPI_COMMON_LIB NFAPI_LIB NFAPI_PNF_LIB NFAPI_USER_LIB
-Wl,--end-group z dl)
......@@ -2745,7 +2717,7 @@ if (${T_TRACER})
SECU_OSA SECU_CN SCHED_LIB SCHED_NR_LIB SCHED_RU_LIB SCHED_UE_LIB
SCHED_NR_UE_LIB NFAPI_COMMON_LIB NFAPI_LIB NFAPI_PNF_LIB NFAPI_VNF_LIB
NFAPI_USER_LIB PHY_COMMON PHY PHY_UE PHY_NR PHY_NR_UE PHY_RU PHY_MEX
L2 L2_NR L2_UE NR_L2_UE MAC_NR_COMMON MAC_NR MAC_UE_NR CN_UTILS GTPV1U
L2 L2_LTE L2_NR L2_UE NR_L2_UE MAC_NR_COMMON MAC_NR MAC_UE_NR CN_UTILS GTPV1U
SCTP_CLIENT UDP LIB_NAS_UE NB_IoT LFDS LFDS7 SIMU SIMU_ETH OPENAIR0_LIB)
if (TARGET ${i})
add_dependencies(${i} generate_T)
......
......@@ -924,6 +924,10 @@ init_opt();
# define PACKAGE_VERSION "UNKNOWN-EXPERIMENTAL"
#endif
LOG_I(HW, "Version: %s\n", PACKAGE_VERSION);
if(IS_SOFTMODEM_NOS1)
init_pdcp();
#if defined(ENABLE_ITTI)
if (RC.nb_nr_inst > 0) {
......@@ -942,9 +946,6 @@ init_opt();
flexran_agent_start(i);
}
if(IS_SOFTMODEM_NOS1)
init_pdcp();
// init UE_PF_PO and mutex lock
pthread_mutex_init(&ue_pf_po_mutex, NULL);
memset (&UE_PF_PO[0][0], 0, sizeof(UE_PF_PO_t)*NUMBER_OF_UE_MAX*MAX_NUM_CCs);
......
......@@ -68,7 +68,7 @@ uint16_t NB_UE_INST = 1;
//Dummy Functions
lte_subframe_t subframe_select(LTE_DL_FRAME_PARMS *frame_parms, unsigned char subframe) {return(SF_DL);}
int rlc_module_init (void) {return(0);}
int rlc_module_init (int enb) {return(0);}
void pdcp_layer_init (void) {}
int rrc_init_nr_global_param (void) {return(0);}
void config_common(int Mod_idP,int CC_idP,int Nid_cell,int nr_bandP,uint64_t SSB_positions,uint16_t ssb_periodicity,uint64_t dl_CarrierFreqP,uint32_t dl_BandwidthP);
......
......@@ -115,7 +115,7 @@ int generate_dlsch_header(unsigned char *mac_header,
unsigned char short_padding,
unsigned short post_padding){return 0;}
uint64_t get_softmodem_optmask(void) {return 0;}
int rlc_module_init (void) {return(0);}
int rlc_module_init (int enb) {return(0);}
void pdcp_layer_init (void) {}
void nr_ip_over_LTE_DRB_preconfiguration(void){}
......
......@@ -146,7 +146,7 @@ void mac_top_init_eNB(void)
RC.mac = mac;
AssertFatal(rlc_module_init() == 0,
AssertFatal(rlc_module_init(1) == 0,
"Could not initialize RLC layer\n");
// These should be out of here later
......@@ -184,7 +184,7 @@ int rlcmac_init_global_param(void)
LOG_I(MAC, "[MAIN] CALLING RLC_MODULE_INIT...\n");
if (rlc_module_init() != 0) {
if (rlc_module_init(1) != 0) {
return (-1);
}
......
......@@ -110,7 +110,7 @@ mac_top_init_ue(int eMBMS_active, char *uecap_xer,
int rlcmac_init_global_param_ue(void) {
LOG_I(MAC, "[MAIN] CALLING RLC_MODULE_INIT...\n");
if (rlc_module_init() != 0) {
if (rlc_module_init(0) != 0) {
return (-1);
}
......
......@@ -47,7 +47,7 @@ nr_l2_init_ue(void)
nr_ue_mac_inst = (NR_UE_MAC_INST_t *)malloc(sizeof(NR_UE_MAC_INST_t)*NB_NR_UE_MAC_INST);
if (IS_SOFTMODEM_NOS1){
if (rlc_module_init() != 0) {
if (rlc_module_init(0) != 0) {
LOG_I(RLC, "Problem at RLC initiation \n");
}
pdcp_layer_init();
......
......@@ -128,7 +128,7 @@ void mac_top_init_gNB(void)
}//END for (i = 0; i < RC.nb_nr_macrlc_inst; i++)
AssertFatal(rlc_module_init() == 0,"Could not initialize RLC layer\n");
AssertFatal(rlc_module_init(1) == 0,"Could not initialize RLC layer\n");
// These should be out of here later
pdcp_layer_init();
......
......@@ -601,7 +601,8 @@ void rlc_data_conf (const protocol_ctxt_t *const ctxt_pP,
}
//-----------------------------------------------------------------------------
int
rlc_module_init (void) {
rlc_module_init (int enb_flag) { /* enb_flag is unused, but needed for binary
* compatibility with rlc_v2 */
//-----------------------------------------------------------------------------
int k;
module_id_t module_id1;
......
......@@ -620,10 +620,10 @@ rlc_op_status_t rlc_stat_req (
unsigned int *const stat_timer_poll_retransmit_timed_out,
unsigned int *const stat_timer_status_prohibit_timed_out);
/*! \fn int rlc_module_init(void)
/*! \fn int rlc_module_init(int enb_flag)
* \brief RAZ the memory of the RLC layer, initialize the memory pool manager (mem_block_t structures mainly used in RLC module).
*/
int rlc_module_init(void);
int rlc_module_init(int enb_flag);
/** @} */
......
CC=gcc
CFLAGS=-Wall -g -Itests
OBJS=nr_rlc_entity.o nr_rlc_entity_am.o nr_rlc_entity_um.o \
nr_rlc_entity_tm.o nr_rlc_sdu.o nr_rlc_pdu.o test.o
PROG=test
$(PROG): $(OBJS)
$(CC) $(CFLAGS) -o $@ $^
%.o: %.c
$(CC) $(CFLAGS) -c -o $@ $<
clean:
rm -f test *.o
RLC AM:
- status reporting (38.322 5.3.4): implement this (not done because not
clearly understood):
"delay triggering the STATUS report until x < RX_Highest_Status
or x >= RX_Next + AM_Window_Size."
- send indication of successful delivery as soon as possible. Today we
signal successful delivery in order. Probably not a big issue though.
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#include "rlc.h"
int decode_t_reordering(int v)
{
static int tab[32] = {
0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85,
90, 95, 100, 110, 120, 130, 140, 150, 160, 170, 180, 190, 200, 1600
};
if (v < 0 || v > 31) {
LOG_E(RLC, "%s:%d:%s: fatal\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
return tab[v];
}
int decode_t_status_prohibit(int v)
{
static int tab[62] = {
0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90,
95, 100, 105, 110, 115, 120, 125, 130, 135, 140, 145, 150, 155, 160, 165,
170, 175, 180, 185, 190, 195, 200, 205, 210, 215, 220, 225, 230, 235, 240,
245, 250, 300, 350, 400, 450, 500, 800, 1000, 1200, 1600, 2000, 2400
};
if (v < 0 || v > 61) {
LOG_E(RLC, "%s:%d:%s: fatal\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
return tab[v];
}
int decode_t_poll_retransmit(int v)
{
static int tab[59] = {
5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95,
100, 105, 110, 115, 120, 125, 130, 135, 140, 145, 150, 155, 160, 165, 170,
175, 180, 185, 190, 195, 200, 205, 210, 215, 220, 225, 230, 235, 240, 245,
250, 300, 350, 400, 450, 500, 800, 1000, 2000, 4000
};
if (v < 0 || v > 58) {
LOG_E(RLC, "%s:%d:%s: fatal\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
return tab[v];
}
int decode_poll_pdu(int v)
{
static int tab[8] = {
4, 8, 16, 32, 64, 128, 256, -1 /* -1 means infinity */
};
if (v < 0 || v > 7) {
LOG_E(RLC, "%s:%d:%s: fatal\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
return tab[v];
}
int decode_poll_byte(int v)
{
static int tab[15] = {
25, 50, 75, 100, 125, 250, 375, 500, 750, 1000, 1250, 1500, 2000, 3000,
-1 /* -1 means infinity */
};
if (v < 0 || v > 14) {
LOG_E(RLC, "%s:%d:%s: fatal\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
if (tab[v] == -1) return -1;
return tab[v] * 1024;
}
int decode_max_retx_threshold(int v)
{
static int tab[8] = {
1, 2, 3, 4, 6, 8, 16, 32
};
if (v < 0 || v > 7) {
LOG_E(RLC, "%s:%d:%s: fatal\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
return tab[v];
}
int decode_sn_field_length(int v)
{
static int tab[2] = {
5, 10
};
if (v < 0 || v > 1) {
LOG_E(RLC, "%s:%d:%s: fatal\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
return tab[v];
}
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#ifndef _ASN1_UTILS_H_
#define _ASN1_UTILS_H_
int decode_t_reordering(int v);
int decode_t_status_prohibit(int v);
int decode_t_poll_retransmit(int v);
int decode_poll_pdu(int v);
int decode_poll_byte(int v);
int decode_max_retx_threshold(int v);
int decode_sn_field_length(int v);
#endif /* _ASN1_UTILS_H_ */
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#include "nr_rlc_entity.h"
#include <stdlib.h>
#include "nr_rlc_entity_am.h"
#include "nr_rlc_entity_um.h"
#include "nr_rlc_entity_tm.h"
#include "LOG/log.h"
nr_rlc_entity_t *new_nr_rlc_entity_am(
int rx_maxsize,
int tx_maxsize,
void (*deliver_sdu)(void *deliver_sdu_data, struct nr_rlc_entity_t *entity,
char *buf, int size),
void *deliver_sdu_data,
void (*sdu_successful_delivery)(void *sdu_successful_delivery_data,
struct nr_rlc_entity_t *entity,
int sdu_id),
void *sdu_successful_delivery_data,
void (*max_retx_reached)(void *max_retx_reached_data,
struct nr_rlc_entity_t *entity),
void *max_retx_reached_data,
int t_poll_retransmit,
int t_reassembly,
int t_status_prohibit,
int poll_pdu,
int poll_byte,
int max_retx_threshold,
int sn_field_length)
{
nr_rlc_entity_am_t *ret;
ret = calloc(1, sizeof(nr_rlc_entity_am_t));
if (ret == NULL) {
LOG_E(RLC, "%s:%d:%s: out of memory\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
ret->tx_maxsize = tx_maxsize;
ret->rx_maxsize = rx_maxsize;
ret->t_poll_retransmit = t_poll_retransmit;
ret->t_reassembly = t_reassembly;
ret->t_status_prohibit = t_status_prohibit;
ret->poll_pdu = poll_pdu;
ret->poll_byte = poll_byte;
ret->max_retx_threshold = max_retx_threshold;
ret->sn_field_length = sn_field_length;
if (!(sn_field_length == 12 || sn_field_length == 18)) {
LOG_E(RLC, "%s:%d:%s: wrong SN field_lenght (%d), must be 12 or 18\n",
__FILE__, __LINE__, __FUNCTION__, sn_field_length);
exit(1);
}
ret->sn_modulus = 1 << ret->sn_field_length;
ret->window_size = ret->sn_modulus / 2;
ret->common.recv_pdu = nr_rlc_entity_am_recv_pdu;
ret->common.buffer_status = nr_rlc_entity_am_buffer_status;
ret->common.generate_pdu = nr_rlc_entity_am_generate_pdu;
ret->common.recv_sdu = nr_rlc_entity_am_recv_sdu;
ret->common.set_time = nr_rlc_entity_am_set_time;
ret->common.discard_sdu = nr_rlc_entity_am_discard_sdu;
ret->common.reestablishment = nr_rlc_entity_am_reestablishment;
ret->common.delete = nr_rlc_entity_am_delete;
ret->common.deliver_sdu = deliver_sdu;
ret->common.deliver_sdu_data = deliver_sdu_data;
ret->common.sdu_successful_delivery = sdu_successful_delivery;
ret->common.sdu_successful_delivery_data = sdu_successful_delivery_data;
ret->common.max_retx_reached = max_retx_reached;
ret->common.max_retx_reached_data = max_retx_reached_data;
return (nr_rlc_entity_t *)ret;
}
nr_rlc_entity_t *new_nr_rlc_entity_um(
int rx_maxsize,
int tx_maxsize,
void (*deliver_sdu)(void *deliver_sdu_data, struct nr_rlc_entity_t *entity,
char *buf, int size),
void *deliver_sdu_data,
int t_reassembly,
int sn_field_length)
{
nr_rlc_entity_um_t *ret;
ret = calloc(1, sizeof(nr_rlc_entity_um_t));
if (ret == NULL) {
LOG_E(RLC, "%s:%d:%s: out of memory\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
ret->tx_maxsize = tx_maxsize;
ret->rx_maxsize = rx_maxsize;
ret->t_reassembly = t_reassembly;
ret->sn_field_length = sn_field_length;
if (!(sn_field_length == 6 || sn_field_length == 12)) {
LOG_E(RLC, "%s:%d:%s: wrong SN field_lenght (%d), must be 6 or 12\n",
__FILE__, __LINE__, __FUNCTION__, sn_field_length);
exit(1);
}
ret->sn_modulus = 1 << ret->sn_field_length;
ret->window_size = ret->sn_modulus / 2;
ret->common.recv_pdu = nr_rlc_entity_um_recv_pdu;
ret->common.buffer_status = nr_rlc_entity_um_buffer_status;
ret->common.generate_pdu = nr_rlc_entity_um_generate_pdu;
ret->common.recv_sdu = nr_rlc_entity_um_recv_sdu;
ret->common.set_time = nr_rlc_entity_um_set_time;
ret->common.discard_sdu = nr_rlc_entity_um_discard_sdu;
ret->common.reestablishment = nr_rlc_entity_um_reestablishment;
ret->common.delete = nr_rlc_entity_um_delete;
ret->common.deliver_sdu = deliver_sdu;
ret->common.deliver_sdu_data = deliver_sdu_data;
return (nr_rlc_entity_t *)ret;
}
nr_rlc_entity_t *new_nr_rlc_entity_tm(
int tx_maxsize,
void (*deliver_sdu)(void *deliver_sdu_data, struct nr_rlc_entity_t *entity,
char *buf, int size),
void *deliver_sdu_data)
{
nr_rlc_entity_tm_t *ret;
ret = calloc(1, sizeof(nr_rlc_entity_tm_t));
if (ret == NULL) {
LOG_E(RLC, "%s:%d:%s: out of memory\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
ret->tx_maxsize = tx_maxsize;
ret->common.recv_pdu = nr_rlc_entity_tm_recv_pdu;
ret->common.buffer_status = nr_rlc_entity_tm_buffer_status;
ret->common.generate_pdu = nr_rlc_entity_tm_generate_pdu;
ret->common.recv_sdu = nr_rlc_entity_tm_recv_sdu;
ret->common.set_time = nr_rlc_entity_tm_set_time;
ret->common.discard_sdu = nr_rlc_entity_tm_discard_sdu;
ret->common.reestablishment = nr_rlc_entity_tm_reestablishment;
ret->common.delete = nr_rlc_entity_tm_delete;
ret->common.deliver_sdu = deliver_sdu;
ret->common.deliver_sdu_data = deliver_sdu_data;
return (nr_rlc_entity_t *)ret;
}
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#ifndef _NR_RLC_ENTITY_H_
#define _NR_RLC_ENTITY_H_
#include <stdint.h>
#define NR_SDU_MAX 16000 /* max NR PDCP SDU size is 9000, let's take more */
typedef struct {
int status_size;
int tx_size;
int retx_size;
} nr_rlc_entity_buffer_status_t;
typedef struct nr_rlc_entity_t {
/* functions provided by the RLC module */
void (*recv_pdu)(struct nr_rlc_entity_t *entity, char *buffer, int size);
nr_rlc_entity_buffer_status_t (*buffer_status)(
struct nr_rlc_entity_t *entity, int maxsize);
int (*generate_pdu)(struct nr_rlc_entity_t *entity, char *buffer, int size);
void (*recv_sdu)(struct nr_rlc_entity_t *entity, char *buffer, int size,
int sdu_id);
void (*set_time)(struct nr_rlc_entity_t *entity, uint64_t now);
void (*discard_sdu)(struct nr_rlc_entity_t *entity, int sdu_id);
void (*reestablishment)(struct nr_rlc_entity_t *entity);
void (*delete)(struct nr_rlc_entity_t *entity);
/* callbacks provided to the RLC module */
void (*deliver_sdu)(void *deliver_sdu_data, struct nr_rlc_entity_t *entity,
char *buf, int size);
void *deliver_sdu_data;
void (*sdu_successful_delivery)(void *sdu_successful_delivery_data,
struct nr_rlc_entity_t *entity,
int sdu_id);
void *sdu_successful_delivery_data;
void (*max_retx_reached)(void *max_retx_reached_data,
struct nr_rlc_entity_t *entity);
void *max_retx_reached_data;
} nr_rlc_entity_t;
nr_rlc_entity_t *new_nr_rlc_entity_am(
int rx_maxsize,
int tx_maxsize,
void (*deliver_sdu)(void *deliver_sdu_data, struct nr_rlc_entity_t *entity,
char *buf, int size),
void *deliver_sdu_data,
void (*sdu_successful_delivery)(void *sdu_successful_delivery_data,
struct nr_rlc_entity_t *entity,
int sdu_id),
void *sdu_successful_delivery_data,
void (*max_retx_reached)(void *max_retx_reached_data,
struct nr_rlc_entity_t *entity),
void *max_retx_reached_data,
int t_poll_retransmit,
int t_reassembly,
int t_status_prohibit,
int poll_pdu,
int poll_byte,
int max_retx_threshold,
int sn_field_length);
nr_rlc_entity_t *new_nr_rlc_entity_um(
int rx_maxsize,
int tx_maxsize,
void (*deliver_sdu)(void *deliver_sdu_data, struct nr_rlc_entity_t *entity,
char *buf, int size),
void *deliver_sdu_data,
int t_reassembly,
int sn_field_length);
nr_rlc_entity_t *new_nr_rlc_entity_tm(
int tx_maxsize,
void (*deliver_sdu)(void *deliver_sdu_data, struct nr_rlc_entity_t *entity,
char *buf, int size),
void *deliver_sdu_data);
#endif /* _NR_RLC_ENTITY_H_ */
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#include "nr_rlc_entity_am.h"
#include <stdlib.h>
#include <string.h>
#include "nr_rlc_pdu.h"
#include "LOG/log.h"
/*************************************************************************/
/* PDU RX functions */
/*************************************************************************/
static int modulus_rx(nr_rlc_entity_am_t *entity, int a)
{
/* as per 38.322 7.1, modulus base is rx_next */
int r = a - entity->rx_next;
if (r < 0) r += entity->sn_modulus;
return r;
}
static int modulus_tx(nr_rlc_entity_am_t *entity, int a)
{
int r = a - entity->tx_next_ack;
if (r < 0) r += entity->sn_modulus;
return r;
}
static int sn_in_recv_window(void *_entity, int sn)
{
nr_rlc_entity_am_t *entity = _entity;
int mod_sn = modulus_rx(entity, sn);
/* we simplify rx_next <= sn < rx_next + am_window_size */
return mod_sn < entity->window_size;
}
static int sn_compare_rx(void *_entity, int a, int b)
{
nr_rlc_entity_am_t *entity = _entity;
return modulus_rx(entity, a) - modulus_rx(entity, b);
}
static int sn_compare_tx(void *_entity, int a, int b)
{
nr_rlc_entity_am_t *entity = _entity;
return modulus_tx(entity, a) - modulus_tx(entity, b);
}
static int segment_already_received(nr_rlc_entity_am_t *entity,
int sn, int so, int size)
{
nr_rlc_pdu_t *l = entity->rx_list;
int covered;
while (l != NULL && size > 0) {
if (l->sn == sn) {
if (l->so <= so && so < l->so + l->size) {
covered = l->size - (so - l->so);
size -= covered;
so += covered;
} else if (l->so <= so+size-1 && so+size-1 < l->so + l->size) {
covered = size - (l->so - so);
size -= covered;
}
}
l = l->next;
}
return size <= 0;
}
static void consider_retransmission(nr_rlc_entity_am_t *entity,
nr_rlc_sdu_segment_t *cur, int update_retx)
{
if (update_retx)
cur->sdu->retx_count++;
/* let's report max RETX reached for all retx_count >= max_retx_threshold
* (specs say to report if retx_count == max_retx_threshold).
* Upper layers should react (radio link failure), so no big deal actually.
*/
if (update_retx && cur->sdu->retx_count >= entity->max_retx_threshold) {
entity->common.max_retx_reached(entity->common.max_retx_reached_data,
(nr_rlc_entity_t *)entity);
}
/* let's put in retransmit list even if we are over max_retx_threshold.
* upper layers should deal with this condition, internally it's better
* for the RLC code to keep going with this segment (we only remove
* a segment that was ACKed)
*/
nr_rlc_sdu_segment_list_append(&entity->retransmit_list,
&entity->retransmit_end,
cur);
}
/* checks that all the bytes of the SDU sn have been received (but SDU
* has not been already processed)
*/
static int sdu_full(nr_rlc_entity_am_t *entity, int sn)
{
nr_rlc_pdu_t *l = entity->rx_list;
int last_byte;
int new_last_byte;
last_byte = -1;
while (l != NULL) {
if (l->sn == sn)
break;
l = l->next;
}
/* check if the data has already been processed */
if (l != NULL && l->data == NULL)
return 0;
while (l != NULL && l->sn == sn) {
if (l->so > last_byte + 1)
return 0;
if (l->is_last)
return 1;
new_last_byte = l->so + l->size - 1;
if (new_last_byte > last_byte)
last_byte = new_last_byte;
l = l->next;
}
return 0;
}
/* checks that an SDU has already been delivered */
static int sdu_delivered(nr_rlc_entity_am_t *entity, int sn)
{
nr_rlc_pdu_t *l = entity->rx_list;
while (l != NULL) {
if (l->sn == sn)
break;
l = l->next;
}
return l != NULL && l->data == NULL;
}
/* check if there is some missing bytes before the last received of SDU sn */
/* todo: be sure that when no byte was received or the SDU has already been
* processed then the SDU has no missing byte
*/
static int sdu_has_missing_bytes(nr_rlc_entity_am_t *entity, int sn)
{
nr_rlc_pdu_t *l = entity->rx_list;
int last_byte;
int new_last_byte;
last_byte = -1;
while (l != NULL) {
if (l->sn == sn)
break;
l = l->next;
}
/* check if the data has already been processed */
if (l != NULL && l->data == NULL)
return 0; /* data already processed: no missing byte */
while (l != NULL && l->sn == sn) {
if (l->so > last_byte + 1)
return 1;
new_last_byte = l->so + l->size - 1;
if (new_last_byte > last_byte)
last_byte = new_last_byte;
l = l->next;
}
return 0;
}
static void reassemble_and_deliver(nr_rlc_entity_am_t *entity, int sn)
{
nr_rlc_pdu_t *pdu;
char sdu[NR_SDU_MAX];
int so = 0;
int bad_sdu = 0;
/* go to first segment of sn */
pdu = entity->rx_list;
while (pdu->sn != sn)
pdu = pdu->next;
/* reassemble - free 'data' of each segment after processing */
while (pdu != NULL && pdu->sn == sn) {
int len = pdu->size - (so - pdu->so);
if (so + len > NR_SDU_MAX && !bad_sdu) {
LOG_E(RLC, "%s:%d:%s: bad SDU, too big, discarding\n",
__FILE__, __LINE__, __FUNCTION__);
bad_sdu = 1;
}
if (!bad_sdu && len > 0) {
memcpy(sdu + so, pdu->data, len);
so += len;
}
free(pdu->data);
pdu->data = NULL;
entity->rx_size -= pdu->size;
pdu = pdu->next;
}
if (bad_sdu)
return;
/* deliver */
entity->common.deliver_sdu(entity->common.deliver_sdu_data,
(nr_rlc_entity_t *)entity,
sdu, so);
}
static void reception_actions(nr_rlc_entity_am_t *entity, nr_rlc_pdu_t *pdu)
{
int x = pdu->sn;
if (sn_compare_rx(entity, x, entity->rx_next_highest) >= 0)
entity->rx_next_highest = (x + 1) % entity->sn_modulus;
/* todo: room for optimization: we can run through rx_list only once */
if (sdu_full(entity, x)) {
reassemble_and_deliver(entity, x);
if (x == entity->rx_highest_status) {
int rx_highest_status = entity->rx_highest_status;
while (sdu_delivered(entity, rx_highest_status))
rx_highest_status = (rx_highest_status + 1) % entity->sn_modulus;
entity->rx_highest_status = rx_highest_status;
}
if (x == entity->rx_next) {
/* update rx_next and free all delivered SDUs at the head of the
* rx_list
*/
int rx_next = entity->rx_next;
while (entity->rx_list != NULL && entity->rx_list->data == NULL &&
entity->rx_list->sn == rx_next) {
/* free all segments of this SDU */
do {
nr_rlc_pdu_t *p = entity->rx_list;
entity->rx_list = p->next;
free(p);
} while (entity->rx_list != NULL &&
entity->rx_list->sn == rx_next);
rx_next = (rx_next + 1) % entity->sn_modulus;
}
entity->rx_next = rx_next;
}
}
if (entity->t_reassembly_start) {
if (entity->rx_next_status_trigger == entity->rx_next ||
(entity->rx_next_status_trigger == (entity->rx_next + 1)
% entity->sn_modulus &&
!sdu_has_missing_bytes(entity, entity->rx_next)) ||
(!sn_in_recv_window(entity, entity->rx_next_status_trigger) &&
entity->rx_next_status_trigger !=
(entity->rx_next + entity->window_size) % entity->sn_modulus)) {
entity->t_reassembly_start = 0;
}
}
if (entity->t_reassembly_start == 0) {
if (sn_compare_rx(entity, entity->rx_next_highest,
(entity->rx_next + 1) % entity->sn_modulus) > 0 ||
(entity->rx_next_highest == (entity->rx_next + 1)
% entity->sn_modulus &&
sdu_has_missing_bytes(entity, entity->rx_next))) {
entity->t_reassembly_start = entity->t_current;
entity->rx_next_status_trigger = entity->rx_next_highest;
}
}
}
static void process_received_ack(nr_rlc_entity_am_t *entity, int ack_sn)
{
nr_rlc_sdu_segment_t head;
nr_rlc_sdu_segment_t *cur;
nr_rlc_sdu_segment_t *prev;
unsigned char sn_set[32768]; /* used to dec retx_count only once per sdu */
memset(sn_set, 0, 32768);
#define IS_SN_SET(b) (sn_set[(b)/8] & (1 << ((b) % 8)))
#define SET_SN(b) do { sn_set[(b)/8] |= (1 << ((b) % 8)); } while (0)
/* put SDUs from wait and retransmit lists with SN < 'ack_sn' to ack_list */
/* process wait list */
head.next = entity->wait_list;
prev = &head;
cur = entity->wait_list;
while (cur != NULL) {
if (sn_compare_tx(entity, cur->sdu->sn, ack_sn) < 0) {
/* remove from wait list */
prev->next = cur->next;
/* put the PDU in the ack list */
entity->ack_list = nr_rlc_sdu_segment_list_add(sn_compare_tx, entity,
entity->ack_list, cur);
entity->wait_end = prev;
cur = prev->next;
} else {
entity->wait_end = cur;
prev = cur;
cur = cur->next;
}
}
entity->wait_list = head.next;
if (entity->wait_list == NULL)
entity->wait_end = NULL;
/* process retransmit list */
head.next = entity->retransmit_list;
prev = &head;
cur = entity->retransmit_list;
while (cur != NULL) {
if (sn_compare_tx(entity, cur->sdu->sn, ack_sn) < 0) {
/* dec. retx_count in case we put this segment back in retransmit list
* in 'process_received_nack'
* do it only once per SDU
*/
if (!IS_SN_SET(cur->sdu->sn)) {
cur->sdu->retx_count--;
SET_SN(cur->sdu->sn);
}
/* remove from retransmit list */
prev->next = cur->next;
/* put the PDU in the ack list */
entity->ack_list = nr_rlc_sdu_segment_list_add(sn_compare_tx, entity,
entity->ack_list, cur);
entity->retransmit_end = prev;
cur = prev->next;
} else {
entity->retransmit_end = cur;
prev = cur;
cur = cur->next;
}
}
entity->retransmit_list = head.next;
if (entity->retransmit_list == NULL)
entity->retransmit_end = NULL;
#undef IS_BIT_SET
#undef SET_BIT
}
static int so_overlap(int s1, int e1, int s2, int e2)
{
if (s1 < s2) {
if (e1 == -1 || e1 >= s2)
return 1;
return 0;
}
if (e2 == -1 || s1 <= e2)
return 1;
return 0;
}
static void process_nack_sn(nr_rlc_entity_am_t *entity, int nack_sn,
int so_start, int so_end, unsigned char *sn_set)
{
/* put all SDU segments with SN == 'sn' and with an overlapping so start/end
* to the retransmit list
* source lists are ack list and wait list.
* Not sure if we should consider wait list, isn't the other end supposed
* to only NACK SNs lower than the ACK SN sent in the status PDU, in which
* case all potential SDU segments should all be in ack list when calling
* the current function? in doubt let's accept anything and thus process
* also wait list.
*/
nr_rlc_sdu_segment_t head;
nr_rlc_sdu_segment_t *cur;
nr_rlc_sdu_segment_t *prev;
#define IS_SN_SET(b) (sn_set[(b)/8] & (1 << ((b) % 8)))
#define SET_SN(b) do { sn_set[(b)/8] |= (1 << ((b) % 8)); } while (0)
/* check that tx_next_ack <= sn < tx_next */
if (!(sn_compare_tx(entity, entity->tx_next_ack, nack_sn) <= 0 &&
sn_compare_tx(entity, nack_sn, entity->tx_next) < 0))
return;
/* process wait list */
head.next = entity->wait_list;
prev = &head;
cur = entity->wait_list;
while (cur != NULL) {
if (cur->sdu->sn == nack_sn &&
so_overlap(so_start, so_end, cur->so, cur->so + cur->size - 1)) {
/* remove from wait list */
prev->next = cur->next;
cur->next = NULL;
/* consider the SDU segment for retransmission */
consider_retransmission(entity, cur, !IS_SN_SET(cur->sdu->sn));
SET_SN(cur->sdu->sn);
entity->wait_end = prev;
cur = prev->next;
} else {
entity->wait_end = cur;
prev = cur;
cur = cur->next;
}
}
entity->wait_list = head.next;
if (entity->wait_list == NULL)
entity->wait_end = NULL;
/* process ack list */
head.next = entity->ack_list;
prev = &head;
cur = entity->ack_list;
while (cur != NULL) {
if (cur->sdu->sn == nack_sn &&
so_overlap(so_start, so_end, cur->so, cur->so + cur->size - 1)) {
/* remove from ack list */
prev->next = cur->next;
cur->next = NULL;
/* consider the SDU segment for retransmission */
consider_retransmission(entity, cur, !IS_SN_SET(cur->sdu->sn));
SET_SN(cur->sdu->sn);
cur = prev->next;
} else {
prev = cur;
cur = cur->next;
}
}
entity->ack_list = head.next;
#undef IS_BIT_SET
#undef SET_BIT
}
static void process_received_nack(nr_rlc_entity_am_t *entity, int nack_sn,
int so_start, int so_end, int range,
unsigned char *sn_set)
{
int i;
for (i = 0; i < range; i++)
process_nack_sn(entity, (nack_sn + i) % entity->sn_modulus,
i == 0 ? so_start : 0,
i == range - 1 ? so_end : -1,
sn_set);
}
static int sdu_segment_in_ack_list_full(nr_rlc_sdu_segment_t *sdu)
{
int target_count = sdu->sdu->ref_count;
int actual_count = 0;
int sn = sdu->sdu->sn;
while (sdu != NULL && sdu->sdu->sn == sn) {
actual_count++;
sdu = sdu->next;
}
return actual_count == target_count;
}
static void finalize_ack_nack_processing(nr_rlc_entity_am_t *entity)
{
nr_rlc_sdu_segment_t *cur = entity->ack_list;
int sn;
/* - send indication of successful delivery for all consecutive acked SDUs
* starting from tx_next_ack. Also free them.
* - update tx_next_ack to the next SN not acked yet
*/
/* todo: send indication of successful delivery as soon as possible as
* the specs say (38.322 5.2.3.1.1). As the code is, if we receive
* ack for SN+2 we won't indicate successful delivery before
* SN+1 has been indicated.
*/
while (cur != NULL && cur->sdu->sn == entity->tx_next_ack &&
sdu_segment_in_ack_list_full(cur)) {
entity->tx_size -= cur->sdu->size;
sn = cur->sdu->sn;
entity->common.sdu_successful_delivery(
entity->common.sdu_successful_delivery_data,
(nr_rlc_entity_t *)entity, cur->sdu->upper_layer_id);
while (cur != NULL && cur->sdu->sn == sn) {
nr_rlc_sdu_segment_t *s = cur;
cur = cur->next;
nr_rlc_free_sdu_segment(s);
}
entity->ack_list = cur;
entity->tx_next_ack = (entity->tx_next_ack + 1) % entity->sn_modulus;
}
}
void nr_rlc_entity_am_recv_pdu(nr_rlc_entity_t *_entity,
char *buffer, int size)
{
#define R(d) do { if (nr_rlc_pdu_decoder_in_error(&d)) goto err; } while (0)
nr_rlc_entity_am_t *entity = (nr_rlc_entity_am_t *)_entity;
nr_rlc_pdu_decoder_t decoder;
nr_rlc_pdu_decoder_t control_decoder;
nr_rlc_pdu_t *pdu;
int dc;
int p = 0;
int si;
int sn;
int so = 0;
int data_size;
int is_first;
int is_last;
int cpt;
int e1;
int e2;
int e3;
int ack_sn;
int nack_sn;
int so_start;
int so_end;
int range;
int control_e1;
int control_e2;
int control_e3;
unsigned char sn_set[32768]; /* used to dec retx_count only once per sdu */
nr_rlc_pdu_decoder_init(&decoder, buffer, size);
dc = nr_rlc_pdu_decoder_get_bits(&decoder, 1); R(decoder);
if (dc == 0) goto control;
/* data PDU */
p = nr_rlc_pdu_decoder_get_bits(&decoder, 1); R(decoder);
si = nr_rlc_pdu_decoder_get_bits(&decoder, 2); R(decoder);
is_first = (si & 0x2) == 0;
is_last = (si & 0x1) == 0;
if (entity->sn_field_length == 18) {
nr_rlc_pdu_decoder_get_bits(&decoder, 2); R(decoder);
}
sn = nr_rlc_pdu_decoder_get_bits(&decoder, entity->sn_field_length);
R(decoder);
if (!is_first) {
so = nr_rlc_pdu_decoder_get_bits(&decoder, 16); R(decoder);
if (so == 0) {
LOG_E(RLC, "%s:%d:%s: warning: discard PDU, bad so\n",
__FILE__, __LINE__, __FUNCTION__);
goto discard;
}
}
data_size = size - decoder.byte;
/* dicard PDU if no data */
if (data_size <= 0) {
LOG_D(RLC, "%s:%d:%s: warning: discard PDU, no data\n",
__FILE__, __LINE__, __FUNCTION__);
goto discard;
}
/* dicard PDU if rx buffer is full */
if (entity->rx_size + data_size > entity->rx_maxsize) {
LOG_D(RLC, "%s:%d:%s: warning: discard PDU, RX buffer full\n",
__FILE__, __LINE__, __FUNCTION__);
goto discard;
}
if (!sn_in_recv_window(entity, sn)) {
LOG_D(RLC, "%s:%d:%s: warning: discard PDU, sn out of window (sn %d rx_next %d)\n",
__FILE__, __LINE__, __FUNCTION__,
sn, entity->rx_next);
goto discard;
}
/* discard segment if all the bytes of the segment are already there */
if (segment_already_received(entity, sn, so, data_size)) {
LOG_D(RLC, "%s:%d:%s: warning: discard PDU, already received\n",
__FILE__, __LINE__, __FUNCTION__);
goto discard;
}
/* put in pdu reception list */
entity->rx_size += data_size;
pdu = nr_rlc_new_pdu(sn, so, is_first, is_last,
buffer + size - data_size, data_size);
entity->rx_list = nr_rlc_pdu_list_add(sn_compare_rx, entity,
entity->rx_list, pdu);
/* do reception actions (38.322 5.2.3.2.3) */
reception_actions(entity, pdu);
if (p) {
/* 38.322 5.3.4 says status triggering should be delayed
* until x < rx_highest_status or x >= rx_next + am_window_size.
* This is not clear (what is x then? we keep the same?). So let's
* trigger no matter what.
* todo: delay status triggering properly
*/
int v = (entity->rx_next + entity->window_size) % entity->sn_modulus;
entity->status_triggered = 1;
if (!(sn_compare_rx(entity, sn, entity->rx_highest_status) < 0 ||
sn_compare_rx(entity, sn, v) >= 0)) {
LOG_D(RLC, "%s:%d:%s: warning: STATUS trigger should be delayed, according to specs\n",
__FILE__, __LINE__, __FUNCTION__);
}
}
return;
control:
cpt = nr_rlc_pdu_decoder_get_bits(&decoder, 3); R(decoder);
if (cpt != 0) {
LOG_D(RLC, "%s:%d:%s: warning: discard PDU, CPT not 0 (%d)\n",
__FILE__, __LINE__, __FUNCTION__, cpt);
goto discard;
}
ack_sn = nr_rlc_pdu_decoder_get_bits(&decoder, entity->sn_field_length); R(decoder);
e1 = nr_rlc_pdu_decoder_get_bits(&decoder, 1); R(decoder);
/* r bits */
if (entity->sn_field_length == 18) {
nr_rlc_pdu_decoder_get_bits(&decoder, 1); R(decoder);
} else {
nr_rlc_pdu_decoder_get_bits(&decoder, 7); R(decoder);
}
/* let's try to parse the control PDU once to check consistency */
control_decoder = decoder;
control_e1 = e1;
while (control_e1) {
nr_rlc_pdu_decoder_get_bits(&control_decoder, entity->sn_field_length); R(control_decoder); /* NACK_SN */
control_e1 = nr_rlc_pdu_decoder_get_bits(&control_decoder, 1); R(control_decoder);
control_e2 = nr_rlc_pdu_decoder_get_bits(&control_decoder, 1); R(control_decoder);
control_e3 = nr_rlc_pdu_decoder_get_bits(&control_decoder, 1); R(control_decoder);
/* r bits */
if (entity->sn_field_length == 18) {
nr_rlc_pdu_decoder_get_bits(&control_decoder, 3); R(control_decoder);
} else {
nr_rlc_pdu_decoder_get_bits(&control_decoder, 1); R(control_decoder);
}
if (control_e2) {
nr_rlc_pdu_decoder_get_bits(&control_decoder, 16); R(control_decoder); /* SOstart */
nr_rlc_pdu_decoder_get_bits(&control_decoder, 16); R(control_decoder); /* SOend */
}
if (control_e3) {
nr_rlc_pdu_decoder_get_bits(&control_decoder, 8); R(control_decoder); /* NACK range */
}
}
/* 38.322 5.3.3.3 says to stop t_poll_retransmit if a ACK or NACK is
* received for the SN 'poll_sn'
*/
if (sn_compare_tx(entity, entity->poll_sn, ack_sn) < 0)
entity->t_poll_retransmit_start = 0;
/* at this point, accept the PDU even if the actual values
* may be incorrect (eg. if so_start > so_end)
*/
process_received_ack(entity, ack_sn);
if (e1)
memset(sn_set, 0, 32768);
while (e1) {
nack_sn = nr_rlc_pdu_decoder_get_bits(&decoder, entity->sn_field_length); R(decoder);
e1 = nr_rlc_pdu_decoder_get_bits(&decoder, 1); R(decoder);
e2 = nr_rlc_pdu_decoder_get_bits(&decoder, 1); R(decoder);
e3 = nr_rlc_pdu_decoder_get_bits(&decoder, 1); R(decoder);
/* r bits */
if (entity->sn_field_length == 18) {
nr_rlc_pdu_decoder_get_bits(&decoder, 3); R(decoder);
} else {
nr_rlc_pdu_decoder_get_bits(&decoder, 1); R(decoder);
}
if (e2) {
so_start = nr_rlc_pdu_decoder_get_bits(&decoder, 16); R(decoder);
so_end = nr_rlc_pdu_decoder_get_bits(&decoder, 16); R(decoder);
if (so_end < so_start) {
LOG_W(RLC, "%s:%d:%s: warning, bad so start/end, NACK the whole PDU (sn %d)\n",
__FILE__, __LINE__, __FUNCTION__, nack_sn);
so_start = 0;
so_end = -1;
}
/* special value 0xffff indicates 'all bytes to the end' */
if (so_end == 0xffff)
so_end = -1;
} else {
so_start = 0;
so_end = -1;
}
if (e3) {
range = nr_rlc_pdu_decoder_get_bits(&decoder, 8); R(decoder);
} else {
range = 1;
}
process_received_nack(entity, nack_sn, so_start, so_end, range, sn_set);
/* 38.322 5.3.3.3 says to stop t_poll_retransmit if a ACK or NACK is
* received for the SN 'poll_sn'
*/
if (sn_compare_tx(entity, nack_sn, entity->poll_sn) <= 0 &&
sn_compare_tx(entity, entity->poll_sn, (nack_sn + range) % entity->sn_modulus) < 0)
entity->t_poll_retransmit_start = 0;
}
finalize_ack_nack_processing(entity);
return;
err:
LOG_W(RLC, "%s:%d:%s: error decoding PDU, discarding\n", __FILE__, __LINE__, __FUNCTION__);
goto discard;
discard:
if (p)
entity->status_triggered = 1;
#undef R
}
/*************************************************************************/
/* TX functions */
/*************************************************************************/
static int is_window_stalling(nr_rlc_entity_am_t *entity)
{
/* we are stalling if tx_next is not:
* tx_next_ack <= tx_next < tx_next_ack + window_size
*/
return !(sn_compare_tx(entity, entity->tx_next_ack, entity->tx_next) <= 0 &&
sn_compare_tx(entity, entity->tx_next,
(entity->tx_next_ack + entity->window_size) %
entity->sn_modulus) < 0);
}
static void include_poll(nr_rlc_entity_am_t *entity, char *buffer)
{
/* set the P bit to 1 */
buffer[0] |= 0x40;
entity->pdu_without_poll = 0;
entity->byte_without_poll = 0;
/* set POLL_SN to highest SN submitted to lower layer
* (this is: entity->tx_next - 1) (todo: be sure of this)
*/
entity->poll_sn = (entity->tx_next - 1 + entity->sn_modulus)
% entity->sn_modulus;
/* start/restart t_poll_retransmit */
entity->t_poll_retransmit_start = entity->t_current;
}
static int check_poll_after_pdu_assembly(nr_rlc_entity_am_t *entity)
{
int retransmission_buffer_empty;
int transmission_buffer_empty;
/* is transmission buffer empty? */
if (entity->tx_list == NULL)
transmission_buffer_empty = 1;
else
transmission_buffer_empty = 0;
/* is retransmission buffer empty? */
if (entity->retransmit_list == NULL)
retransmission_buffer_empty = 1;
else
retransmission_buffer_empty = 0;
return (transmission_buffer_empty && retransmission_buffer_empty) ||
is_window_stalling(entity);
}
static int serialize_sdu(nr_rlc_entity_am_t *entity,
nr_rlc_sdu_segment_t *sdu, char *buffer, int bufsize,
int p)
{
nr_rlc_pdu_encoder_t encoder;
/* generate header */
nr_rlc_pdu_encoder_init(&encoder, buffer, bufsize);
nr_rlc_pdu_encoder_put_bits(&encoder, 1, 1); /* D/C: 1 = data */
nr_rlc_pdu_encoder_put_bits(&encoder, 0, 1); /* P: reserve, set later */
nr_rlc_pdu_encoder_put_bits(&encoder, 1-sdu->is_first,1);/* 1st bit of SI */
nr_rlc_pdu_encoder_put_bits(&encoder, 1-sdu->is_last,1); /* 2nd bit of SI */
if (entity->sn_field_length == 18)
nr_rlc_pdu_encoder_put_bits(&encoder, 0, 2); /* R */
nr_rlc_pdu_encoder_put_bits(&encoder, sdu->sdu->sn,
entity->sn_field_length); /* SN */
if (!sdu->is_first)
nr_rlc_pdu_encoder_put_bits(&encoder, sdu->so, 16); /* SO */
/* data */
memcpy(buffer + encoder.byte, sdu->sdu->data + sdu->so, sdu->size);
if (p)
include_poll(entity, buffer);
return encoder.byte + sdu->size;
}
/* for a given SDU/SDU segment, computes the corresponding PDU header size */
static int compute_pdu_header_size(nr_rlc_entity_am_t *entity,
nr_rlc_sdu_segment_t *sdu)
{
int header_size = 2;
/* one more byte if SN field length is 18 */
if (entity->sn_field_length == 18)
header_size++;
/* two more bytes for SO if SDU segment is not the first */
if (!sdu->is_first) header_size += 2;
return header_size;
}
/* resize SDU/SDU segment for the corresponding PDU to fit into 'pdu_size'
* bytes
* - modifies SDU/SDU segment to become an SDU segment
* - returns a new SDU segment covering the remaining data bytes
*/
static nr_rlc_sdu_segment_t *resegment(nr_rlc_sdu_segment_t *sdu,
nr_rlc_entity_am_t *entity,
int pdu_size)
{
nr_rlc_sdu_segment_t *next;
int pdu_header_size;
int over_size;
sdu->sdu->ref_count++;
pdu_header_size = compute_pdu_header_size(entity, sdu);
next = calloc(1, sizeof(nr_rlc_sdu_segment_t));
if (next == NULL) {
LOG_E(RLC, "%s:%d:%s: out of memory\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
*next = *sdu;
over_size = pdu_header_size + sdu->size - pdu_size;
/* update SDU */
sdu->size -= over_size;
sdu->is_last = 0;
/* create new segment */
next->size = over_size;
next->so = sdu->so + sdu->size;
next->is_first = 0;
return next;
}
/*************************************************************************/
/* TX functions - status reporting [begin] */
/*************************************************************************/
typedef struct {
/* data for missing bytes */
int sn_start; /* set to -1 when no more missing part to report */
int so_start;
int sn_end;
int so_end;
/* data for maximum ack */
int ack_sn; /* -1 if not to be used */
/* pdu to use for next call to 'next_missing' */
nr_rlc_pdu_t *next;
} missing_data_t;
/* todo: rewrite this function, too messy */
static missing_data_t next_missing(nr_rlc_entity_am_t *entity,
nr_rlc_pdu_t *cur, int check_head)
{
missing_data_t ret;
int cur_max_so;
int sn;
int max_so = 0;
int last_reached = 0;
ret.ack_sn = -1;
/* special case: missing part before the head of RX list */
if (check_head) {
if (cur->sn != entity->rx_next || !cur->is_first) {
/* don't report if out of reporting window */
if (sn_compare_rx(entity, entity->rx_highest_status, cur->sn) <= 0) {
ret.sn_start = -1;
return ret;
}
/* the missing part is starting from rx_next(0)
* going to min of:
* - cur->sn(cur->so-1) [if cur->sn is not first]
* or (cur->sn-1)(0xffff) [if cur->sn is first]
* - (entity->rx_highest_status-1)(0xffff)
*/
ret.sn_start = entity->rx_next;
ret.so_start = 0;
goto set_end_different_sdu;
}
}
next_pdu:
sn = cur->sn;
cur_max_so = cur->so + cur->size - 1;
if (cur_max_so > max_so)
max_so = cur_max_so;
last_reached = last_reached | cur->is_last;
/* if cur already processed, it can be the acked SDU */
if (cur->data == NULL)
ret.ack_sn = (cur->sn + 1) % entity->sn_modulus;
/* no next? */
if (cur->next == NULL) {
/* inform the caller that work is over */
ret.next = NULL;
/* already processed => next SDU to rx_highest_status - 1 to be nacked */
if (cur->data == NULL) {
ret.sn_start = (cur->sn + 1) % entity->sn_modulus;
/* don't report if out of reporting window */
if (sn_compare_rx(entity, entity->rx_highest_status,
ret.sn_start) <= 0) {
ret.sn_start = -1;
return ret;
}
ret.so_start = 0;
ret.sn_end = (entity->rx_highest_status - 1 + entity->sn_modulus) %
entity->sn_modulus;
ret.so_end = 0xffff;
return ret;
}
/* not already processed => all bytes after max_so (if any) then all SDU
* to rx_highest_status-1 to be nacked
*/
if (last_reached) {
ret.sn_start = (cur->sn + 1) % entity->sn_modulus;
ret.so_start = 0;
} else {
ret.sn_start = cur->sn;
ret.so_start = max_so + 1;
}
/* don't report if out of reporting window */
if (sn_compare_rx(entity, entity->rx_highest_status,
ret.sn_start) <= 0) {
ret.sn_start = -1;
return ret;
}
ret.sn_end = (entity->rx_highest_status - 1 + entity->sn_modulus) %
entity->sn_modulus;
ret.so_end = 0xffff;
return ret;
}
cur = cur->next;
/* no discontinuity in data => process to next PDU */
if (cur->sn == sn && max_so >= cur->so - 1)
goto next_pdu;
if (cur->sn == (sn + 1) % entity->sn_modulus && last_reached &&
cur->is_first) {
last_reached = 0;
max_so = 0;
goto next_pdu;
}
/* discontinuity in data */
/* remember where to start from for the next call */
ret.next = cur;
/* discontinuity in same SDU */
if (cur->sn == sn) {
ret.sn_start = sn;
/* don't report if out of reporting window */
if (sn_compare_rx(entity, entity->rx_highest_status,
ret.sn_start) <= 0) {
ret.sn_start = -1;
return ret;
}
ret.so_start = max_so + 1;
ret.sn_end = sn;
ret.so_end = cur->so - 1;
return ret;
}
/* discontinuity between different SDUs */
ret.sn_start = sn;
/* don't report if out of reporting window */
if (sn_compare_rx(entity, entity->rx_highest_status, ret.sn_start) <= 0) {
ret.sn_start = -1;
return ret;
}
ret.so_start = max_so + 1;
set_end_different_sdu:
/* don't go more than rx_highest_status - 1 */
if (sn_compare_rx(entity, entity->rx_highest_status, cur->sn) <= 0) {
ret.so_end = (entity->rx_highest_status - 1 + entity->sn_modulus) %
entity->sn_modulus;
ret.so_end = 0xffff;
return ret;
}
/* if cur is the head of a SDU, then use cur-1 */
if (cur->is_first) {
ret.sn_end = (cur->sn - 1 + entity->sn_modulus) % entity->sn_modulus;
ret.so_end = 0xffff;
return ret;
}
ret.sn_end = cur->sn;
ret.so_end = cur->so - 1;
return ret;
}
static int nack_size(nr_rlc_entity_am_t *entity, missing_data_t *m)
{
int nack_length = 2 + (entity->sn_field_length == 18);
if (m->sn_start == m->sn_end) {
/* only nack_sn, no so_start/end, no nack range */
if (m->so_start == 0 && m->so_end == 0xffff)
return nack_length;
/* nack_sn + so_start/end */
return nack_length + 4;
}
/* nack_sn + nack range, no so_start/end */
if (m->so_start == 0 && m->so_end == 0xffff)
return nack_length + 1;
/* nack_sn + so_start/end + nack range */
return nack_length + 5;
}
/* returns the e1 byte/bit position supposing the encoder points at
* the beginning of a nack_sn block
*/
static void get_e1_position(nr_rlc_entity_am_t *entity,
nr_rlc_pdu_encoder_t *encoder,
int *e1_byte, int *e1_bit)
{
if (entity->sn_field_length == 18) {
*e1_byte = encoder->byte + 2;
*e1_bit = 5;
} else {
*e1_byte = encoder->byte + 1;
*e1_bit = 3;
}
}
/* returns the number of nacks serialized.
* In most cases it is 1, it can be more if the
* missing data consists of a range that is more
* than 255 SNs in which case it has to be cut in
* smaller ranges.
* If there is no more room in the status buffer,
* will set m->next = NULL (and may serialize
* less nacks than required by 'm').
*/
static int generate_missing(nr_rlc_entity_am_t *entity,
nr_rlc_pdu_encoder_t *encoder,
missing_data_t *m, int *e1_byte, int *e1_bit)
{
int r_bits = entity->sn_field_length == 18 ? 3 : 1;
int range_count = 0;
int sn_start;
int so_start;
int sn_end;
int so_end;
int sn_count;
missing_data_t m_nack;
int e2;
int e3;
/* be careful to limit a range to 255 SNs, that is: cut if needed */
sn_count = (m->sn_end - m->sn_start + entity->sn_modulus)
% entity->sn_modulus + 1;
sn_start = m->sn_start;
while (sn_count) {
int cur_sn_count = sn_count;
if (cur_sn_count > 255)
cur_sn_count = 255;
/* for first range, so_start is the one of the initial range
* for the following ones, it is 0
*/
if (sn_start == m->sn_start) {
/* first range */
so_start = m->so_start;
} else {
/* following ranges */
so_start = 0;
}
/* for the last range, sn_end/so_end are the ones of the initial range
* for the previous ones, it is sn_start+254/0xffff
*/
if (cur_sn_count == sn_count) {
/* last range */
sn_end = m->sn_end;
so_end = m->so_end;
} else {
/* previous ranges */
sn_end = (sn_start + 254) % entity->sn_modulus;
so_end = 0xffff;
}
/* check that there is room for a nack */
m_nack.sn_start = sn_start;
m_nack.so_start = so_start;
m_nack.sn_end = sn_end;
m_nack.so_end = so_end;
if (encoder->byte + nack_size(entity, &m_nack) > encoder->size) {
m->next = NULL;
break;
}
/* set the previous e1 bit to 1 */
encoder->buffer[*e1_byte] |= 1 << *e1_bit;
get_e1_position(entity, encoder, e1_byte, e1_bit);
if (sn_start == sn_end) {
if (so_start == 0 && so_end == 0xffff) {
/* only nack_sn, no so_start/end, no nack range */
e2 = 0;
e3 = 0;
} else {
/* nack_sn + so_start/end, no nack range */
e2 = 1;
e3 = 0;
}
} else {
if (so_start == 0 && so_end == 0xffff) {
/* nack_sn + nack range, no so_start/end */
e2 = 0;
e3 = 1;
} else {
/* nack_sn + so_start/end + nack range */
e2 = 1;
e3 = 1;
}
}
/* nack_sn */
nr_rlc_pdu_encoder_put_bits(encoder, sn_start,
entity->sn_field_length);
/* e1 = 0 (set later if needed) */
nr_rlc_pdu_encoder_put_bits(encoder, 0, 1);
/* e2 */
nr_rlc_pdu_encoder_put_bits(encoder, e2, 1);
/* e3 */
nr_rlc_pdu_encoder_put_bits(encoder, e3, 1);
/* r */
nr_rlc_pdu_encoder_put_bits(encoder, 0, r_bits);
/* so_start/so_end */
if (e2) {
nr_rlc_pdu_encoder_put_bits(encoder, so_start, 16);
nr_rlc_pdu_encoder_put_bits(encoder, so_end, 16);
}
/* nack range */
if (e3)
nr_rlc_pdu_encoder_put_bits(encoder, cur_sn_count, 8);
sn_count -= cur_sn_count;
sn_start = (sn_start + cur_sn_count) % entity->sn_modulus;
range_count++;
}
return range_count;
}
static int generate_status(nr_rlc_entity_am_t *entity, char *buffer, int size)
{
int ack_sn = entity->rx_next;
missing_data_t m;
nr_rlc_pdu_t *cur;
int nack_count = 0;
nr_rlc_pdu_encoder_t encoder;
int e1_byte;
int e1_bit;
/* if not enough room, do nothing */
if (size < 3)
return 0;
nr_rlc_pdu_encoder_init(&encoder, buffer, size);
/* first 3 bytes, ack_sn and e1 will be set later */
nr_rlc_pdu_encoder_put_bits(&encoder, 0, 8*3);
cur = entity->rx_list;
/* store the position of the e1 bit to be set if
* there is a nack following
*/
e1_byte = 2;
e1_bit = entity->sn_field_length == 18 ? 1 : 7;
while (cur != NULL) {
m = next_missing(entity, cur, nack_count == 0);
/* update ack_sn if the returned value is valid */
if (m.ack_sn != -1)
ack_sn = m.ack_sn;
/* stop here if no more nack to report */
if (m.sn_start == -1)
break;
nack_count += generate_missing(entity, &encoder, &m, &e1_byte, &e1_bit);
cur = m.next;
}
/* put ack_sn */
if (entity->sn_field_length == 12) {
buffer[0] = ack_sn >> 8;
buffer[1] = ack_sn & 255;
} else {
buffer[0] = ack_sn >> 14;
buffer[1] = (ack_sn >> 6) & 255;
buffer[2] |= (ack_sn & 0x3f) << 2;
}
/* reset the trigger */
entity->status_triggered = 0;
/* start t_status_prohibit */
entity->t_status_prohibit_start = entity->t_current;
return encoder.byte;
}
static int status_to_report(nr_rlc_entity_am_t *entity)
{
return entity->status_triggered &&
(entity->t_status_prohibit_start == 0 ||
entity->t_current - entity->t_status_prohibit_start >
entity->t_status_prohibit);
}
static int missing_size(nr_rlc_entity_am_t *entity, missing_data_t *m,
int *size, int maxsize)
{
int r_bits = entity->sn_field_length == 18 ? 3 : 1;
int range_count = 0;
int sn_start;
int so_start;
int sn_end;
int so_end;
int sn_count;
missing_data_t m_nack;
/* be careful to limit a range to 255 SNs, that is: cut if needed */
sn_count = m->sn_end - m->sn_start;
if (sn_count < 0)
sn_count += entity->sn_modulus;
sn_start = m->sn_start;
while (sn_count) {
int cur_sn_count = sn_count;
if (cur_sn_count > 255)
cur_sn_count = 255;
/* for first range, so_start is the one of the initial range
* for the following ones, it is 0
*/
if (sn_start == m->sn_start) {
/* first range */
so_start = m->so_start;
} else {
/* following ranges */
so_start = 0;
}
/* for the last range, sn_end/so_end are the ones of the initial range
* for the previous ones, it is sn_start+254/0xffff
*/
if (cur_sn_count == sn_count) {
/* last range */
sn_end = m->sn_end;
so_end = m->so_end;
} else {
/* previous ranges */
sn_end = (sn_start + 254) % entity->sn_modulus;
so_end = 0xffff;
}
/* check that there is room for a nack */
m_nack.sn_start = sn_start;
m_nack.so_start = so_start;
m_nack.sn_end = sn_end;
m_nack.so_end = so_end;
if (*size + nack_size(entity, &m_nack) > maxsize) {
m->next = NULL;
break;
}
if (sn_start == sn_end) {
if (so_start == 0 && so_end == 0xffff) {
/* only nack_sn, no so_start/end, no nack range */
*size += (entity->sn_field_length + 3 + r_bits) / 8;
} else {
/* nack_sn + so_start/end, no nack range */
*size += (entity->sn_field_length + 3 + r_bits + 16*2) / 8;
}
} else {
if (so_start == 0 && so_end == 0xffff) {
/* nack_sn + nack range, no so_start/end */
*size += (entity->sn_field_length + 3 + r_bits + 8) / 8;
} else {
/* nack_sn + so_start/end + nack range */
*size += (entity->sn_field_length + 3 + r_bits + 16*2 + 8) / 8;
}
}
sn_count -= cur_sn_count;
sn_start = (sn_start + cur_sn_count) % entity->sn_modulus;
range_count++;
}
return range_count;
}
static int status_size(nr_rlc_entity_am_t *entity, int maxsize)
{
missing_data_t m;
nr_rlc_pdu_t *cur;
int nack_count = 0;
int size;
/* if not enough room, do nothing */
if (maxsize < 3)
return 0;
/* minimum 3 bytes */
size = 3;
cur = entity->rx_list;
while (cur != NULL) {
m = next_missing(entity, cur, nack_count == 0);
/* stop here if no more nack to report */
if (m.sn_start == -1)
break;
nack_count += missing_size(entity, &m, &size, maxsize);
cur = m.next;
}
return size;
}
/*************************************************************************/
/* TX functions - status reporting [end] */
/*************************************************************************/
static int generate_retx_pdu(nr_rlc_entity_am_t *entity, char *buffer,
int size)
{
nr_rlc_sdu_segment_t *sdu;
int pdu_header_size;
int pdu_size;
int p;
sdu = entity->retransmit_list;
pdu_header_size = compute_pdu_header_size(entity, sdu);
/* not enough room for at least one byte of data? do nothing */
if (pdu_header_size + 1 > size)
return 0;
entity->retransmit_list = entity->retransmit_list->next;
if (entity->retransmit_list == NULL)
entity->retransmit_end = NULL;
sdu->next = NULL;
/* segment if necessary */
pdu_size = pdu_header_size + sdu->size;
if (pdu_size > size) {
nr_rlc_sdu_segment_t *next_sdu;
next_sdu = resegment(sdu, entity, size);
/* put the second SDU back at the head of the retransmit list */
next_sdu->next = entity->retransmit_list;
entity->retransmit_list = next_sdu;
if (entity->retransmit_end == NULL)
entity->retransmit_end = entity->retransmit_list;
}
/* put SDU/SDU segment in the wait list */
nr_rlc_sdu_segment_list_append(&entity->wait_list, &entity->wait_end, sdu);
p = check_poll_after_pdu_assembly(entity);
if (entity->force_poll) {
p = 1;
entity->force_poll = 0;
}
return serialize_sdu(entity, sdu, buffer, size, p);
}
static int generate_tx_pdu(nr_rlc_entity_am_t *entity, char *buffer, int size)
{
nr_rlc_sdu_segment_t *sdu;
int pdu_header_size;
int pdu_size;
int p;
/* sn out of window (that is: we have window stalling)? do nothing */
if (is_window_stalling(entity))
return 0;
if (entity->tx_list == NULL)
return 0;
sdu = entity->tx_list;
pdu_header_size = compute_pdu_header_size(entity, sdu);
/* not enough room for at least one byte of data? do nothing */
if (pdu_header_size + 1 > size)
return 0;
entity->tx_list = entity->tx_list->next;
if (entity->tx_list == NULL)
entity->tx_end = NULL;
sdu->next = NULL;
/* assign SN to SDU */
sdu->sdu->sn = entity->tx_next;
/* segment if necessary */
pdu_size = pdu_header_size + sdu->size;
if (pdu_size > size) {
nr_rlc_sdu_segment_t *next_sdu;
next_sdu = resegment(sdu, entity, size);
/* put the second SDU back at the head of the TX list */
next_sdu->next = entity->tx_list;
entity->tx_list = next_sdu;
if (entity->tx_end == NULL)
entity->tx_end = entity->tx_list;
}
/* update tx_next if the SDU segment is the last */
if (sdu->is_last)
entity->tx_next = (entity->tx_next + 1) % entity->sn_modulus;
/* put SDU/SDU segment in the wait list */
nr_rlc_sdu_segment_list_append(&entity->wait_list, &entity->wait_end, sdu);
/* polling actions for a new PDU */
entity->pdu_without_poll++;
entity->byte_without_poll += sdu->size;
if ((entity->poll_pdu != -1 &&
entity->pdu_without_poll >= entity->poll_pdu) ||
(entity->poll_byte != -1 &&
entity->byte_without_poll >= entity->poll_byte))
p = 1;
else
p = check_poll_after_pdu_assembly(entity);
if (entity->force_poll) {
p = 1;
entity->force_poll = 0;
}
return serialize_sdu(entity, sdu, buffer, size, p);
}
/* Pretend to serialize all the SDUs in a list and return the size
* of all the PDUs it would produce, limited to 'maxsize'.
* Used for buffer status reporting.
*/
static int tx_list_size(nr_rlc_entity_am_t *entity,
nr_rlc_sdu_segment_t *l, int maxsize)
{
int ret = 0;
while (l != NULL) {
ret += compute_pdu_header_size(entity, l) + l->size;
l = l->next;
}
if (ret > maxsize) ret = maxsize;
return ret;
}
nr_rlc_entity_buffer_status_t nr_rlc_entity_am_buffer_status(
nr_rlc_entity_t *_entity, int maxsize)
{
nr_rlc_entity_am_t *entity = (nr_rlc_entity_am_t *)_entity;
nr_rlc_entity_buffer_status_t ret;
if (status_to_report(entity))
ret.status_size = status_size(entity, maxsize);
else
ret.status_size = 0;
ret.tx_size = tx_list_size(entity, entity->tx_list, maxsize);
ret.retx_size = tx_list_size(entity, entity->retransmit_list, maxsize);
return ret;
}
int nr_rlc_entity_am_generate_pdu(nr_rlc_entity_t *_entity,
char *buffer, int size)
{
nr_rlc_entity_am_t *entity = (nr_rlc_entity_am_t *)_entity;
int ret;
if (status_to_report(entity)) {
ret = generate_status(entity, buffer, size);
if (ret != 0)
return ret;
}
if (entity->retransmit_list != NULL) {
ret = generate_retx_pdu(entity, buffer, size);
if (ret != 0)
return ret;
}
return generate_tx_pdu(entity, buffer, size);
}
/*************************************************************************/
/* SDU RX functions */
/*************************************************************************/
void nr_rlc_entity_am_recv_sdu(nr_rlc_entity_t *_entity,
char *buffer, int size,
int sdu_id)
{
nr_rlc_entity_am_t *entity = (nr_rlc_entity_am_t *)_entity;
nr_rlc_sdu_segment_t *sdu;
if (size > NR_SDU_MAX) {
LOG_E(RLC, "%s:%d:%s: fatal: SDU size too big (%d bytes)\n",
__FILE__, __LINE__, __FUNCTION__, size);
exit(1);
}
if (entity->tx_size + size > entity->tx_maxsize) {
LOG_D(RLC, "%s:%d:%s: warning: SDU rejected, SDU buffer full\n",
__FILE__, __LINE__, __FUNCTION__);
return;
}
entity->tx_size += size;
sdu = nr_rlc_new_sdu(buffer, size, sdu_id);
nr_rlc_sdu_segment_list_append(&entity->tx_list, &entity->tx_end, sdu);
}
/*************************************************************************/
/* time/timers */
/*************************************************************************/
static void check_t_poll_retransmit(nr_rlc_entity_am_t *entity)
{
nr_rlc_sdu_segment_t head;
nr_rlc_sdu_segment_t *cur;
nr_rlc_sdu_segment_t *prev;
int sn;
int old_retx_count;
/* 38.322 5.3.3.4 */
/* did t_poll_retransmit expire? */
if (entity->t_poll_retransmit_start == 0 ||
entity->t_current <= entity->t_poll_retransmit_start +
entity->t_poll_retransmit)
return;
/* stop timer */
entity->t_poll_retransmit_start = 0;
/* 38.322 5.3.3.4 says:
*
* - include a poll in a RLC data PDU as described in section 5.3.3.2
*
* That does not seem to be conditional. So we forcefully will send
* a poll as soon as we generate a PDU.
* Hopefully this interpretation is correct. In the worst case we generate
* more polling than necessary, but it's not a big deal. When
* 't_poll_retransmit' expires it means we didn't receive a status report,
* meaning a bad radio link, so things are quite bad at this point and
* asking again for a poll won't hurt much more.
*/
entity->force_poll = 1;
LOG_D(RLC, "%s:%d:%s: warning: t_poll_retransmit expired\n",
__FILE__, __LINE__, __FUNCTION__);
/* do we meet conditions of 38.322 5.3.3.4? */
if (!check_poll_after_pdu_assembly(entity))
return;
/* search wait list for SDU with highest SN */
/* this code may be incorrect: in LTE we had to look for PDU
* with SN = VT(S) - 1, but for NR the specs say "highest SN among the
* ones submitted to lower layers" not 'tx_next - 1'. So we should look
* for the highest SN in the wait list. But that's no big deal. If the
* program runs this code, then the connection is in a bad state and we
* can retransmit whatever we want. At some point we will receive a status
* report and retransmit what we really have to. Actually we could just
* retransmit the head of wait list (the specs have this 'or').
* (Actually, maybe this interpretation is not correct and what the code
* does is correct. The specs are confusing.)
*/
sn = (entity->tx_next - 1 + entity->sn_modulus) % entity->sn_modulus;
head.next = entity->wait_list;
cur = entity->wait_list;
prev = &head;
while (cur != NULL) {
if (cur->sdu->sn == sn)
break;
prev = cur;
cur = cur->next;
}
/* SDU with highest SN not found? take the head of wait list */
if (cur == NULL) {
cur = entity->wait_list;
prev = &head;
sn = cur->sdu->sn;
}
/* todo: do we need to for check cur == NULL?
* It seems that no, the wait list should not be empty here, but not sure.
*/
old_retx_count = cur->sdu->retx_count;
/* 38.322 says "SDU", not "SDU segment", so let's retransmit all
* SDU segments with this SN
*/
/* todo: maybe we could simply retransmit the current SDU segment,
* so that we don't have to run through the full wait list.
*/
while (cur != NULL) {
if (cur->sdu->sn == sn) {
prev->next = cur->next;
cur->next = NULL;
/* put in retransmit list */
consider_retransmission(entity, cur,
old_retx_count == cur->sdu->retx_count);
} else {
prev = cur;
}
cur = prev->next;
}
entity->wait_list = head.next;
/* reset wait_end (todo: optimize?) */
entity->wait_end = entity->wait_list;
while (entity->wait_end != NULL && entity->wait_end->next != NULL)
entity->wait_end = entity->wait_end->next;
}
static void check_t_reassembly(nr_rlc_entity_am_t *entity)
{
int sn;
/* is t_reassembly running and if yes has it expired? */
if (entity->t_reassembly_start == 0 ||
entity->t_current <= entity->t_reassembly_start + entity->t_reassembly)
return;
/* stop timer */
entity->t_reassembly_start = 0;
LOG_D(RLC, "%s:%d:%s: t_reassembly expired\n",
__FILE__, __LINE__, __FUNCTION__);
/* update RX_Highest_Status */
sn = entity->rx_next_status_trigger;
while (sdu_delivered(entity, sn))
sn = (sn + 1) % entity->sn_modulus;
entity->rx_highest_status = sn;
if (sn_compare_rx(entity, entity->rx_next_highest,
(entity->rx_highest_status+1) % entity->sn_modulus) > 0 ||
(entity->rx_next_highest ==
(entity->rx_highest_status+1) % entity->sn_modulus &&
sdu_has_missing_bytes(entity, entity->rx_highest_status))) {
entity->t_reassembly_start = entity->t_current;
entity->rx_next_status_trigger = entity->rx_next_highest;
}
}
void nr_rlc_entity_am_set_time(nr_rlc_entity_t *_entity, uint64_t now)
{
nr_rlc_entity_am_t *entity = (nr_rlc_entity_am_t *)_entity;
entity->t_current = now;
check_t_poll_retransmit(entity);
check_t_reassembly(entity);
}
/*************************************************************************/
/* discard/re-establishment/delete */
/*************************************************************************/
void nr_rlc_entity_am_discard_sdu(nr_rlc_entity_t *_entity, int sdu_id)
{
/* implements 38.322 5.4 */
nr_rlc_entity_am_t *entity = (nr_rlc_entity_am_t *)_entity;
nr_rlc_sdu_segment_t head;
nr_rlc_sdu_segment_t *cur;
nr_rlc_sdu_segment_t *prev;
head.next = entity->tx_list;
cur = entity->tx_list;
prev = &head;
while (cur != NULL && cur->sdu->upper_layer_id != sdu_id) {
prev = cur;
cur = cur->next;
}
/* if sdu_id not found or some bytes have already been 'PDU-ized'
* then do nothing
*/
if (cur == NULL || !cur->is_first || !cur->is_last)
return;
/* remove SDU from tx_list */
prev->next = cur->next;
entity->tx_list = head.next;
if (entity->tx_end == cur) {
if (prev != &head)
entity->tx_end = prev;
else
entity->tx_end = NULL;
}
nr_rlc_free_sdu_segment(cur);
}
static void clear_entity(nr_rlc_entity_am_t *entity)
{
nr_rlc_pdu_t *cur_rx;
entity->rx_next = 0;
entity->rx_next_status_trigger = 0;
entity->rx_highest_status = 0;
entity->rx_next_highest = 0;
entity->status_triggered = 0;
entity->tx_next = 0;
entity->tx_next_ack = 0;
entity->poll_sn = 0;
entity->pdu_without_poll = 0;
entity->byte_without_poll = 0;
entity->force_poll = 0;
entity->t_current = 0;
entity->t_poll_retransmit_start = 0;
entity->t_reassembly_start = 0;
entity->t_status_prohibit_start = 0;
cur_rx = entity->rx_list;
while (cur_rx != NULL) {
nr_rlc_pdu_t *p = cur_rx;
cur_rx = cur_rx->next;
nr_rlc_free_pdu(p);
}
entity->rx_list = NULL;
entity->rx_size = 0;
nr_rlc_free_sdu_segment_list(entity->tx_list);
nr_rlc_free_sdu_segment_list(entity->wait_list);
nr_rlc_free_sdu_segment_list(entity->retransmit_list);
nr_rlc_free_sdu_segment_list(entity->ack_list);
entity->tx_list = NULL;
entity->tx_end = NULL;
entity->tx_size = 0;
entity->wait_list = NULL;
entity->wait_end = NULL;
entity->retransmit_list = NULL;
entity->retransmit_end = NULL;
entity->ack_list = NULL;
}
void nr_rlc_entity_am_reestablishment(nr_rlc_entity_t *_entity)
{
nr_rlc_entity_am_t *entity = (nr_rlc_entity_am_t *)_entity;
clear_entity(entity);
}
void nr_rlc_entity_am_delete(nr_rlc_entity_t *_entity)
{
nr_rlc_entity_am_t *entity = (nr_rlc_entity_am_t *)_entity;
clear_entity(entity);
free(entity);
}
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#ifndef _NR_RLC_ENTITY_AM_H_
#define _NR_RLC_ENTITY_AM_H_
#include "nr_rlc_entity.h"
#include "nr_rlc_sdu.h"
#include "nr_rlc_pdu.h"
typedef struct {
nr_rlc_entity_t common;
/* configuration */
int t_poll_retransmit;
int t_reassembly;
int t_status_prohibit;
int poll_pdu; /* -1 means infinity */
int poll_byte; /* -1 means infinity */
int max_retx_threshold;
int sn_field_length;
int sn_modulus;
int window_size;
/* runtime rx */
int rx_next;
int rx_next_status_trigger;
int rx_highest_status;
int rx_next_highest;
int status_triggered;
/* runtime tx */
int tx_next;
int tx_next_ack;
int poll_sn;
int pdu_without_poll;
int byte_without_poll;
int force_poll;
/* set to the latest know time by the user of the module. Unit: ms */
uint64_t t_current;
/* timers (stores the TTI of activation, 0 means not active) */
uint64_t t_poll_retransmit_start;
uint64_t t_reassembly_start;
uint64_t t_status_prohibit_start;
/* rx management */
nr_rlc_pdu_t *rx_list;
int rx_size;
int rx_maxsize;
/* tx management */
nr_rlc_sdu_segment_t *tx_list;
nr_rlc_sdu_segment_t *tx_end;
int tx_size;
int tx_maxsize;
nr_rlc_sdu_segment_t *wait_list;
nr_rlc_sdu_segment_t *wait_end;
nr_rlc_sdu_segment_t *retransmit_list;
nr_rlc_sdu_segment_t *retransmit_end;
nr_rlc_sdu_segment_t *ack_list;
} nr_rlc_entity_am_t;
void nr_rlc_entity_am_recv_sdu(nr_rlc_entity_t *entity,
char *buffer, int size,
int sdu_id);
void nr_rlc_entity_am_recv_pdu(nr_rlc_entity_t *entity,
char *buffer, int size);
nr_rlc_entity_buffer_status_t nr_rlc_entity_am_buffer_status(
nr_rlc_entity_t *entity, int maxsize);
int nr_rlc_entity_am_generate_pdu(nr_rlc_entity_t *entity,
char *buffer, int size);
void nr_rlc_entity_am_set_time(nr_rlc_entity_t *entity, uint64_t now);
void nr_rlc_entity_am_discard_sdu(nr_rlc_entity_t *_entity, int sdu_id);
void nr_rlc_entity_am_reestablishment(nr_rlc_entity_t *_entity);
void nr_rlc_entity_am_delete(nr_rlc_entity_t *entity);
#endif /* _NR_RLC_ENTITY_AM_H_ */
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#include "nr_rlc_entity_tm.h"
#include <stdlib.h>
#include <string.h>
#include "nr_rlc_pdu.h"
#include "LOG/log.h"
/*************************************************************************/
/* PDU RX functions */
/*************************************************************************/
void nr_rlc_entity_tm_recv_pdu(nr_rlc_entity_t *_entity,
char *buffer, int size)
{
nr_rlc_entity_tm_t *entity = (nr_rlc_entity_tm_t *)_entity;
entity->common.deliver_sdu(entity->common.deliver_sdu_data,
(nr_rlc_entity_t *)entity,
buffer, size);
}
/*************************************************************************/
/* TX functions */
/*************************************************************************/
static int generate_tx_pdu(nr_rlc_entity_tm_t *entity, char *buffer, int size)
{
nr_rlc_sdu_segment_t *sdu;
int ret;
if (entity->tx_list == NULL)
return 0;
sdu = entity->tx_list;
/* not enough room? do nothing */
if (sdu->size > size)
return 0;
entity->tx_list = entity->tx_list->next;
if (entity->tx_list == NULL)
entity->tx_end = NULL;
ret = sdu->size;
memcpy(buffer, sdu->sdu->data, sdu->size);
entity->tx_size -= sdu->size;
nr_rlc_free_sdu_segment(sdu);
return ret;
}
static int tx_list_size(nr_rlc_entity_tm_t *entity,
nr_rlc_sdu_segment_t *l, int maxsize)
{
int ret = 0;
while (l != NULL) {
ret += l->size;
l = l->next;
}
if (ret > maxsize) ret = maxsize;
return ret;
}
nr_rlc_entity_buffer_status_t nr_rlc_entity_tm_buffer_status(
nr_rlc_entity_t *_entity, int maxsize)
{
nr_rlc_entity_tm_t *entity = (nr_rlc_entity_tm_t *)_entity;
nr_rlc_entity_buffer_status_t ret;
ret.status_size = 0;
ret.tx_size = tx_list_size(entity, entity->tx_list, maxsize);
ret.retx_size = 0;
return ret;
}
int nr_rlc_entity_tm_generate_pdu(nr_rlc_entity_t *_entity,
char *buffer, int size)
{
nr_rlc_entity_tm_t *entity = (nr_rlc_entity_tm_t *)_entity;
return generate_tx_pdu(entity, buffer, size);
}
/*************************************************************************/
/* SDU RX functions */
/*************************************************************************/
void nr_rlc_entity_tm_recv_sdu(nr_rlc_entity_t *_entity,
char *buffer, int size,
int sdu_id)
{
nr_rlc_entity_tm_t *entity = (nr_rlc_entity_tm_t *)_entity;
nr_rlc_sdu_segment_t *sdu;
if (size > NR_SDU_MAX) {
LOG_E(RLC, "%s:%d:%s: fatal: SDU size too big (%d bytes)\n",
__FILE__, __LINE__, __FUNCTION__, size);
exit(1);
}
if (entity->tx_size + size > entity->tx_maxsize) {
LOG_D(RLC, "%s:%d:%s: warning: SDU rejected, SDU buffer full\n",
__FILE__, __LINE__, __FUNCTION__);
return;
}
entity->tx_size += size;
sdu = nr_rlc_new_sdu(buffer, size, sdu_id);
nr_rlc_sdu_segment_list_append(&entity->tx_list, &entity->tx_end, sdu);
}
/*************************************************************************/
/* time/timers */
/*************************************************************************/
void nr_rlc_entity_tm_set_time(nr_rlc_entity_t *_entity, uint64_t now)
{
nr_rlc_entity_tm_t *entity = (nr_rlc_entity_tm_t *)_entity;
entity->t_current = now;
}
/*************************************************************************/
/* discard/re-establishment/delete */
/*************************************************************************/
void nr_rlc_entity_tm_discard_sdu(nr_rlc_entity_t *_entity, int sdu_id)
{
/* nothing to do */
}
static void clear_entity(nr_rlc_entity_tm_t *entity)
{
nr_rlc_free_sdu_segment_list(entity->tx_list);
entity->tx_list = NULL;
entity->tx_end = NULL;
entity->tx_size = 0;
}
void nr_rlc_entity_tm_reestablishment(nr_rlc_entity_t *_entity)
{
nr_rlc_entity_tm_t *entity = (nr_rlc_entity_tm_t *)_entity;
clear_entity(entity);
}
void nr_rlc_entity_tm_delete(nr_rlc_entity_t *_entity)
{
nr_rlc_entity_tm_t *entity = (nr_rlc_entity_tm_t *)_entity;
clear_entity(entity);
free(entity);
}
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#ifndef _NR_RLC_ENTITY_TM_H_
#define _NR_RLC_ENTITY_TM_H_
#include "nr_rlc_entity.h"
#include "nr_rlc_sdu.h"
typedef struct {
nr_rlc_entity_t common;
/* set to the latest know time by the user of the module. Unit: ms */
uint64_t t_current;
/* tx management */
nr_rlc_sdu_segment_t *tx_list;
nr_rlc_sdu_segment_t *tx_end;
int tx_size;
int tx_maxsize;
} nr_rlc_entity_tm_t;
void nr_rlc_entity_tm_recv_sdu(nr_rlc_entity_t *entity,
char *buffer, int size,
int sdu_id);
void nr_rlc_entity_tm_recv_pdu(nr_rlc_entity_t *entity,
char *buffer, int size);
nr_rlc_entity_buffer_status_t nr_rlc_entity_tm_buffer_status(
nr_rlc_entity_t *entity, int maxsize);
int nr_rlc_entity_tm_generate_pdu(nr_rlc_entity_t *entity,
char *buffer, int size);
void nr_rlc_entity_tm_set_time(nr_rlc_entity_t *entity, uint64_t now);
void nr_rlc_entity_tm_discard_sdu(nr_rlc_entity_t *_entity, int sdu_id);
void nr_rlc_entity_tm_reestablishment(nr_rlc_entity_t *_entity);
void nr_rlc_entity_tm_delete(nr_rlc_entity_t *entity);
#endif /* _NR_RLC_ENTITY_TM_H_ */
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#include "nr_rlc_entity_um.h"
#include <stdlib.h>
#include <string.h>
#include "nr_rlc_pdu.h"
#include "LOG/log.h"
/*************************************************************************/
/* PDU RX functions */
/*************************************************************************/
static int modulus_rx(nr_rlc_entity_um_t *entity, int a)
{
/* as per 38.322 7.1, modulus base is rx_next_highest - window_size */
int r = a - (entity->rx_next_highest - entity->window_size);
if (r < 0) r += entity->sn_modulus;
return r % entity->sn_modulus;
}
static int sn_compare_rx(void *_entity, int a, int b)
{
nr_rlc_entity_um_t *entity = _entity;
return modulus_rx(entity, a) - modulus_rx(entity, b);
}
/* checks that all the bytes of the SDU sn have been received (but SDU
* has not been already processed)
*/
static int sdu_full(nr_rlc_entity_um_t *entity, int sn)
{
nr_rlc_pdu_t *l = entity->rx_list;
int last_byte;
int new_last_byte;
last_byte = -1;
while (l != NULL) {
if (l->sn == sn)
break;
l = l->next;
}
/* check if the data has already been processed */
if (l != NULL && l->data == NULL)
return 0;
while (l != NULL && l->sn == sn) {
if (l->so > last_byte + 1)
return 0;
if (l->is_last)
return 1;
new_last_byte = l->so + l->size - 1;
if (new_last_byte > last_byte)
last_byte = new_last_byte;
l = l->next;
}
return 0;
}
/* checks that an SDU has already been delivered */
static int sdu_delivered(nr_rlc_entity_um_t *entity, int sn)
{
nr_rlc_pdu_t *l = entity->rx_list;
while (l != NULL) {
if (l->sn == sn)
break;
l = l->next;
}
return l != NULL && l->data == NULL;
}
/* check if there is some missing bytes before the last received of SDU sn */
/* todo: be sure that when no byte was received or the SDU has already been
* processed then the SDU has no missing byte
*/
static int sdu_has_missing_bytes(nr_rlc_entity_um_t *entity, int sn)
{
nr_rlc_pdu_t *l = entity->rx_list;
int last_byte;
int new_last_byte;
last_byte = -1;
while (l != NULL) {
if (l->sn == sn)
break;
l = l->next;
}
/* check if the data has already been processed */
if (l != NULL && l->data == NULL)
return 0; /* data already processed: no missing byte */
while (l != NULL && l->sn == sn) {
if (l->so > last_byte + 1)
return 1;
new_last_byte = l->so + l->size - 1;
if (new_last_byte > last_byte)
last_byte = new_last_byte;
l = l->next;
}
return 0;
}
static void reassemble_and_deliver(nr_rlc_entity_um_t *entity, int sn)
{
nr_rlc_pdu_t *pdu;
char sdu[NR_SDU_MAX];
int so = 0;
int bad_sdu = 0;
/* go to first segment of sn */
pdu = entity->rx_list;
while (pdu->sn != sn)
pdu = pdu->next;
/* reassemble - free 'data' of each segment after processing */
while (pdu != NULL && pdu->sn == sn) {
int len = pdu->size - (so - pdu->so);
if (so + len > NR_SDU_MAX && !bad_sdu) {
LOG_E(RLC, "%s:%d:%s: bad SDU, too big, discarding\n",
__FILE__, __LINE__, __FUNCTION__);
bad_sdu = 1;
}
if (!bad_sdu && len > 0) {
memcpy(sdu + so, pdu->data, len);
so += len;
}
free(pdu->data);
pdu->data = NULL;
entity->rx_size -= pdu->size;
pdu->size = 0;
pdu = pdu->next;
}
if (bad_sdu)
return;
/* deliver */
entity->common.deliver_sdu(entity->common.deliver_sdu_data,
(nr_rlc_entity_t *)entity,
sdu, so);
}
static void reception_actions(nr_rlc_entity_um_t *entity, nr_rlc_pdu_t *pdu)
{
int x = pdu->sn;
if (sdu_full(entity, x)) {
/* SDU full */
reassemble_and_deliver(entity, x);
if (x == entity->rx_next_reassembly) {
int rx_next_reassembly = entity->rx_next_reassembly;
while (sdu_delivered(entity, rx_next_reassembly))
rx_next_reassembly = (rx_next_reassembly + 1) % entity->sn_modulus;
entity->rx_next_reassembly = rx_next_reassembly;
}
} else {
/* SDU not full */
/* test if x is not in reassembly window, that is x >= rx_next_highest */
if (sn_compare_rx(entity, x, entity->rx_next_highest) >= 0) {
entity->rx_next_highest = (x + 1) % entity->sn_modulus;
/* discard PDUs not in reassembly window */
while (entity->rx_list != NULL &&
sn_compare_rx(entity, entity->rx_list->sn,
entity->rx_next_highest) >= 0) {
nr_rlc_pdu_t *p = entity->rx_list;
entity->rx_size -= p->size;
entity->rx_list = p->next;
nr_rlc_free_pdu(p);
}
/* if rx_next_reassembly not in reassembly window */
if (sn_compare_rx(entity, entity->rx_next_reassembly,
entity->rx_next_highest) >= 0) {
int rx_next_reassembly;
/* set rx_next_reassembly to first SN >= rx_next_highest - window_size
* not delivered yet
*/
rx_next_reassembly = (entity->rx_next_highest - entity->window_size
+ entity->sn_modulus) % entity->sn_modulus;
while (sdu_delivered(entity, rx_next_reassembly))
rx_next_reassembly = (rx_next_reassembly + 1) % entity->sn_modulus;
entity->rx_next_reassembly = rx_next_reassembly;
}
}
}
if (entity->t_reassembly_start) {
if (/* rx_timer_trigger <= rx_next_reassembly */
sn_compare_rx(entity, entity->rx_timer_trigger,
entity->rx_next_reassembly) <= 0 ||
/* or rx_timer_trigger outside of reassembly window and not equal
* to rx_next_highest, that is is > rx_next_highest
*/
sn_compare_rx(entity, entity->rx_timer_trigger,
entity->rx_next_highest) > 0 ||
/* or rx_next_highest == rx_next_reassembly + 1 and no missing byte
* for rx_next_reassembly
*/
(entity->rx_next_highest == (entity->rx_next_reassembly + 1) %
entity->sn_modulus &&
!sdu_has_missing_bytes(entity, entity->rx_next_reassembly)))
entity->t_reassembly_start = 0;
}
if (entity->t_reassembly_start == 0) {
if (sn_compare_rx(entity, entity->rx_next_highest,
(entity->rx_next_reassembly + 1)
% entity->sn_modulus) > 0 ||
(entity->rx_next_highest == (entity->rx_next_reassembly + 1)
% entity->sn_modulus &&
sdu_has_missing_bytes(entity, entity->rx_next_reassembly))) {
entity->t_reassembly_start = entity->t_current;
entity->rx_timer_trigger = entity->rx_next_highest;
}
}
}
void nr_rlc_entity_um_recv_pdu(nr_rlc_entity_t *_entity,
char *buffer, int size)
{
#define R(d) do { if (nr_rlc_pdu_decoder_in_error(&d)) goto err; } while (0)
nr_rlc_entity_um_t *entity = (nr_rlc_entity_um_t *)_entity;
nr_rlc_pdu_decoder_t decoder;
nr_rlc_pdu_t *pdu;
int si;
int sn;
int so = 0;
int data_size;
int is_first;
int is_last;
nr_rlc_pdu_decoder_init(&decoder, buffer, size);
si = nr_rlc_pdu_decoder_get_bits(&decoder, 2); R(decoder);
is_first = (si & 0x2) == 0;
is_last = (si & 0x1) == 0;
/* if full, deliver SDU */
if (is_first && is_last) {
if (size < 2) {
LOG_E(RLC, "%s:%d:%s: warning: discard PDU, no data\n",
__FILE__, __LINE__, __FUNCTION__);
goto discard;
}
/* deliver */
entity->common.deliver_sdu(entity->common.deliver_sdu_data,
(nr_rlc_entity_t *)entity,
buffer + 1, size - 1);
return;
}
if (entity->sn_field_length == 12) {
nr_rlc_pdu_decoder_get_bits(&decoder, 2); R(decoder);
}
sn = nr_rlc_pdu_decoder_get_bits(&decoder, entity->sn_field_length);
R(decoder);
if (!is_first) {
so = nr_rlc_pdu_decoder_get_bits(&decoder, 16); R(decoder);
if (so == 0) {
LOG_E(RLC, "%s:%d:%s: warning: discard PDU, bad so\n",
__FILE__, __LINE__, __FUNCTION__);
goto discard;
}
}
data_size = size - decoder.byte;
/* dicard PDU if no data */
if (data_size <= 0) {
LOG_D(RLC, "%s:%d:%s: warning: discard PDU, no data\n",
__FILE__, __LINE__, __FUNCTION__);
goto discard;
}
/* dicard PDU if rx buffer is full */
if (entity->rx_size + data_size > entity->rx_maxsize) {
LOG_D(RLC, "%s:%d:%s: warning: discard PDU, RX buffer full\n",
__FILE__, __LINE__, __FUNCTION__);
goto discard;
}
/* discard PDU if sn < rx_next_reassembly */
if (sn_compare_rx(entity, sn, entity->rx_next_reassembly) < 0) {
LOG_D(RLC, "%s:%d:%s: warning: discard PDU, SN (%d) < rx_next_reassembly (%d)\n",
__FILE__, __LINE__, __FUNCTION__,
sn, entity->rx_next_reassembly);
goto discard;
}
/* put in pdu reception list */
entity->rx_size += data_size;
pdu = nr_rlc_new_pdu(sn, so, is_first, is_last,
buffer + size - data_size, data_size);
entity->rx_list = nr_rlc_pdu_list_add(sn_compare_rx, entity,
entity->rx_list, pdu);
/* do reception actions (38.322 5.2.2.2.3) */
reception_actions(entity, pdu);
return;
err:
LOG_W(RLC, "%s:%d:%s: error decoding PDU, discarding\n", __FILE__, __LINE__, __FUNCTION__);
goto discard;
discard:
return;
#undef R
}
/*************************************************************************/
/* TX functions */
/*************************************************************************/
static int serialize_sdu(nr_rlc_entity_um_t *entity,
nr_rlc_sdu_segment_t *sdu, char *buffer, int bufsize)
{
nr_rlc_pdu_encoder_t encoder;
/* generate header */
nr_rlc_pdu_encoder_init(&encoder, buffer, bufsize);
nr_rlc_pdu_encoder_put_bits(&encoder, 1-sdu->is_first,1);/* 1st bit of SI */
nr_rlc_pdu_encoder_put_bits(&encoder, 1-sdu->is_last,1); /* 2nd bit of SI */
/* SN, if required */
if (sdu->is_first == 1 && sdu->is_last == 1) {
nr_rlc_pdu_encoder_put_bits(&encoder, 0, 6); /* R */
} else {
if (entity->sn_field_length == 12)
nr_rlc_pdu_encoder_put_bits(&encoder, 0, 2); /* R */
nr_rlc_pdu_encoder_put_bits(&encoder, sdu->sdu->sn,
entity->sn_field_length); /* SN */
}
if (!sdu->is_first)
nr_rlc_pdu_encoder_put_bits(&encoder, sdu->so, 16); /* SO */
/* data */
memcpy(buffer + encoder.byte, sdu->sdu->data + sdu->so, sdu->size);
return encoder.byte + sdu->size;
}
/* for a given SDU/SDU segment, computes the corresponding PDU header size */
static int compute_pdu_header_size(nr_rlc_entity_um_t *entity,
nr_rlc_sdu_segment_t *sdu)
{
int header_size = 1;
/* if SN to be included then one more byte if SN field length is 12 */
if (!(sdu->is_first && sdu->is_last) && entity->sn_field_length == 12)
header_size++;
/* two more bytes for SO if SDU segment is not the first */
if (!sdu->is_first) header_size += 2;
return header_size;
}
/* resize SDU/SDU segment for the corresponding PDU to fit into 'pdu_size'
* bytes
* - modifies SDU/SDU segment to become an SDU segment
* - returns a new SDU segment covering the remaining data bytes
* returns NULL if pdu_size is too small to contain the new segment
*/
static nr_rlc_sdu_segment_t *resegment(nr_rlc_sdu_segment_t *sdu,
nr_rlc_entity_um_t *entity,
int pdu_size)
{
nr_rlc_sdu_segment_t *next;
int pdu_header_size;
int over_size;
int old_is_last;
sdu->sdu->ref_count++;
/* clear is_last to compute header size */
old_is_last = sdu->is_last;
sdu->is_last = 0;
pdu_header_size = compute_pdu_header_size(entity, sdu);
sdu->is_last = old_is_last;
/* if no room for at least 1 data byte, do nothing */
if (pdu_header_size + 1 > pdu_size)
return NULL;
next = calloc(1, sizeof(nr_rlc_sdu_segment_t));
if (next == NULL) {
LOG_E(RLC, "%s:%d:%s: out of memory\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
*next = *sdu;
over_size = pdu_header_size + sdu->size - pdu_size;
/* update SDU */
sdu->size -= over_size;
sdu->is_last = 0;
/* create new segment */
next->size = over_size;
next->so = sdu->so + sdu->size;
next->is_first = 0;
return next;
}
static int generate_tx_pdu(nr_rlc_entity_um_t *entity, char *buffer, int size)
{
nr_rlc_sdu_segment_t *sdu;
int pdu_header_size;
int pdu_size;
int ret;
if (entity->tx_list == NULL)
return 0;
sdu = entity->tx_list;
pdu_header_size = compute_pdu_header_size(entity, sdu);
/* not enough room for at least one byte of data? do nothing */
if (pdu_header_size + 1 > size)
return 0;
entity->tx_list = entity->tx_list->next;
if (entity->tx_list == NULL)
entity->tx_end = NULL;
/* assign SN to SDU */
sdu->sdu->sn = entity->tx_next;
/* segment if necessary */
pdu_size = pdu_header_size + sdu->size;
if (pdu_size > size) {
nr_rlc_sdu_segment_t *next_sdu;
next_sdu = resegment(sdu, entity, size);
if (next_sdu == NULL)
return 0;
/* put the second SDU back at the head of the TX list */
next_sdu->next = entity->tx_list;
entity->tx_list = next_sdu;
if (entity->tx_end == NULL)
entity->tx_end = entity->tx_list;
}
/* update tx_next if the SDU is an SDU segment and is the last */
if (!sdu->is_first && sdu->is_last)
entity->tx_next = (entity->tx_next + 1) % entity->sn_modulus;
ret = serialize_sdu(entity, sdu, buffer, size);
entity->tx_size -= sdu->size;
nr_rlc_free_sdu_segment(sdu);
return ret;
}
/* Pretend to serialize all the SDUs in a list and return the size
* of all the PDUs it would produce, limited to 'maxsize'.
* Used for buffer status reporting.
*/
static int tx_list_size(nr_rlc_entity_um_t *entity,
nr_rlc_sdu_segment_t *l, int maxsize)
{
int ret = 0;
while (l != NULL) {
ret += compute_pdu_header_size(entity, l) + l->size;
l = l->next;
}
if (ret > maxsize) ret = maxsize;
return ret;
}
nr_rlc_entity_buffer_status_t nr_rlc_entity_um_buffer_status(
nr_rlc_entity_t *_entity, int maxsize)
{
nr_rlc_entity_um_t *entity = (nr_rlc_entity_um_t *)_entity;
nr_rlc_entity_buffer_status_t ret;
ret.status_size = 0;
ret.tx_size = tx_list_size(entity, entity->tx_list, maxsize);
ret.retx_size = 0;
return ret;
}
int nr_rlc_entity_um_generate_pdu(nr_rlc_entity_t *_entity,
char *buffer, int size)
{
nr_rlc_entity_um_t *entity = (nr_rlc_entity_um_t *)_entity;
return generate_tx_pdu(entity, buffer, size);
}
/*************************************************************************/
/* SDU RX functions */
/*************************************************************************/
void nr_rlc_entity_um_recv_sdu(nr_rlc_entity_t *_entity,
char *buffer, int size,
int sdu_id)
{
nr_rlc_entity_um_t *entity = (nr_rlc_entity_um_t *)_entity;
nr_rlc_sdu_segment_t *sdu;
if (size > NR_SDU_MAX) {
LOG_E(RLC, "%s:%d:%s: fatal: SDU size too big (%d bytes)\n",
__FILE__, __LINE__, __FUNCTION__, size);
exit(1);
}
if (entity->tx_size + size > entity->tx_maxsize) {
LOG_D(RLC, "%s:%d:%s: warning: SDU rejected, SDU buffer full\n",
__FILE__, __LINE__, __FUNCTION__);
return;
}
entity->tx_size += size;
sdu = nr_rlc_new_sdu(buffer, size, sdu_id);
nr_rlc_sdu_segment_list_append(&entity->tx_list, &entity->tx_end, sdu);
}
/*************************************************************************/
/* time/timers */
/*************************************************************************/
static void check_t_reassembly(nr_rlc_entity_um_t *entity)
{
nr_rlc_pdu_t *cur;
/* is t_reassembly running and if yes has it expired? */
if (entity->t_reassembly_start == 0 ||
entity->t_current <= entity->t_reassembly_start + entity->t_reassembly)
return;
/* stop timer */
entity->t_reassembly_start = 0;
LOG_D(RLC, "%s:%d:%s: t_reassembly expired\n",
__FILE__, __LINE__, __FUNCTION__);
/* update rx_next_reassembly to first SN >= rx_timer_trigger not reassembled
* (ie. not delivered yet)
*/
entity->rx_next_reassembly = entity->rx_timer_trigger;
while (sdu_delivered(entity, entity->rx_next_reassembly))
entity->rx_next_reassembly = (entity->rx_next_reassembly + 1)
% entity->sn_modulus;
/* discard all segments < entity->rx_next_reassembly */
cur = entity->rx_list;
while (cur != NULL &&
sn_compare_rx(entity, cur->sn, entity->rx_next_reassembly) < 0) {
nr_rlc_free_pdu(cur);
cur = cur->next;
entity->rx_list = cur;
}
if (sn_compare_rx(entity, entity->rx_next_highest,
(entity->rx_next_reassembly + 1)
% entity->sn_modulus) > 0 ||
(entity->rx_next_highest == entity->rx_next_reassembly + 1 &&
sdu_has_missing_bytes(entity, entity->rx_next_reassembly))) {
entity->t_reassembly_start = entity->t_current;
entity->rx_timer_trigger = entity->rx_next_highest;
}
}
void nr_rlc_entity_um_set_time(nr_rlc_entity_t *_entity, uint64_t now)
{
nr_rlc_entity_um_t *entity = (nr_rlc_entity_um_t *)_entity;
entity->t_current = now;
check_t_reassembly(entity);
}
/*************************************************************************/
/* discard/re-establishment/delete */
/*************************************************************************/
void nr_rlc_entity_um_discard_sdu(nr_rlc_entity_t *_entity, int sdu_id)
{
/* implements 38.322 5.4 */
nr_rlc_entity_um_t *entity = (nr_rlc_entity_um_t *)_entity;
nr_rlc_sdu_segment_t head;
nr_rlc_sdu_segment_t *cur;
nr_rlc_sdu_segment_t *prev;
head.next = entity->tx_list;
cur = entity->tx_list;
prev = &head;
while (cur != NULL && cur->sdu->upper_layer_id != sdu_id) {
prev = cur;
cur = cur->next;
}
/* if sdu_id not found or some bytes have already been 'PDU-ized'
* then do nothing
*/
if (cur == NULL || !cur->is_first || !cur->is_last)
return;
/* remove SDU from tx_list */
prev->next = cur->next;
entity->tx_list = head.next;
if (entity->tx_end == cur) {
if (prev != &head)
entity->tx_end = prev;
else
entity->tx_end = NULL;
}
nr_rlc_free_sdu_segment(cur);
}
static void clear_entity(nr_rlc_entity_um_t *entity)
{
nr_rlc_pdu_t *cur_rx;
entity->rx_next_highest = 0;
entity->rx_next_reassembly = 0;
entity->rx_timer_trigger = 0;
entity->tx_next = 0;
entity->t_current = 0;
entity->t_reassembly_start = 0;
cur_rx = entity->rx_list;
while (cur_rx != NULL) {
nr_rlc_pdu_t *p = cur_rx;
cur_rx = cur_rx->next;
nr_rlc_free_pdu(p);
}
entity->rx_list = NULL;
entity->rx_size = 0;
nr_rlc_free_sdu_segment_list(entity->tx_list);
entity->tx_list = NULL;
entity->tx_end = NULL;
entity->tx_size = 0;
}
void nr_rlc_entity_um_reestablishment(nr_rlc_entity_t *_entity)
{
nr_rlc_entity_um_t *entity = (nr_rlc_entity_um_t *)_entity;
clear_entity(entity);
}
void nr_rlc_entity_um_delete(nr_rlc_entity_t *_entity)
{
nr_rlc_entity_um_t *entity = (nr_rlc_entity_um_t *)_entity;
clear_entity(entity);
free(entity);
}
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#ifndef _NR_RLC_ENTITY_UM_H_
#define _NR_RLC_ENTITY_UM_H_
#include "nr_rlc_entity.h"
#include "nr_rlc_sdu.h"
#include "nr_rlc_pdu.h"
typedef struct {
nr_rlc_entity_t common;
/* configuration */
int t_reassembly;
int sn_field_length;
int sn_modulus;
int window_size;
/* runtime rx */
int rx_next_highest;
int rx_next_reassembly;
int rx_timer_trigger;
/* runtime tx */
int tx_next;
/* set to the latest know time by the user of the module. Unit: ms */
uint64_t t_current;
/* timers (stores the TTI of activation, 0 means not active) */
uint64_t t_reassembly_start;
/* rx management */
nr_rlc_pdu_t *rx_list;
int rx_size;
int rx_maxsize;
/* tx management */
nr_rlc_sdu_segment_t *tx_list;
nr_rlc_sdu_segment_t *tx_end;
int tx_size;
int tx_maxsize;
} nr_rlc_entity_um_t;
void nr_rlc_entity_um_recv_sdu(nr_rlc_entity_t *entity,
char *buffer, int size,
int sdu_id);
void nr_rlc_entity_um_recv_pdu(nr_rlc_entity_t *entity,
char *buffer, int size);
nr_rlc_entity_buffer_status_t nr_rlc_entity_um_buffer_status(
nr_rlc_entity_t *entity, int maxsize);
int nr_rlc_entity_um_generate_pdu(nr_rlc_entity_t *entity,
char *buffer, int size);
void nr_rlc_entity_um_set_time(nr_rlc_entity_t *entity, uint64_t now);
void nr_rlc_entity_um_discard_sdu(nr_rlc_entity_t *_entity, int sdu_id);
void nr_rlc_entity_um_reestablishment(nr_rlc_entity_t *_entity);
void nr_rlc_entity_um_delete(nr_rlc_entity_t *entity);
#endif /* _NR_RLC_ENTITY_UM_H_ */
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
/* from openair */
#include "rlc.h"
#include "pdcp.h"
/* from nr rlc module */
#include "asn1_utils.h"
#include "nr_rlc_ue_manager.h"
#include "nr_rlc_entity.h"
#include <stdint.h>
static nr_rlc_ue_manager_t *nr_rlc_ue_manager;
/* TODO: handle time a bit more properly */
static uint64_t nr_rlc_current_time;
static int nr_rlc_current_time_last_frame;
static int nr_rlc_current_time_last_subframe;
void mac_rlc_data_ind (
const module_id_t module_idP,
const rnti_t rntiP,
const eNB_index_t eNB_index,
const frame_t frameP,
const eNB_flag_t enb_flagP,
const MBMS_flag_t MBMS_flagP,
const logical_chan_id_t channel_idP,
char *buffer_pP,
const tb_size_t tb_sizeP,
num_tb_t num_tbP,
crc_t *crcs_pP)
{
nr_rlc_ue_t *ue;
nr_rlc_entity_t *rb;
if (module_idP != 0 || eNB_index != 0 || /*enb_flagP != 1 ||*/ MBMS_flagP != 0) {
LOG_E(RLC, "%s:%d:%s: fatal\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
if (enb_flagP)
T(T_ENB_RLC_MAC_UL, T_INT(module_idP), T_INT(rntiP),
T_INT(channel_idP), T_INT(tb_sizeP));
nr_rlc_manager_lock(nr_rlc_ue_manager);
ue = nr_rlc_manager_get_ue(nr_rlc_ue_manager, rntiP);
switch (channel_idP) {
case 1 ... 2: rb = ue->srb[channel_idP - 1]; break;
case 3 ... 7: rb = ue->drb[channel_idP - 3]; break;
default: rb = NULL; break;
}
if (rb != NULL) {
rb->set_time(rb, nr_rlc_current_time);
rb->recv_pdu(rb, buffer_pP, tb_sizeP);
} else {
LOG_E(RLC, "%s:%d:%s: fatal: no RB found (channel ID %d)\n",
__FILE__, __LINE__, __FUNCTION__, channel_idP);
exit(1);
}
nr_rlc_manager_unlock(nr_rlc_ue_manager);
}
tbs_size_t mac_rlc_data_req(
const module_id_t module_idP,
const rnti_t rntiP,
const eNB_index_t eNB_index,
const frame_t frameP,
const eNB_flag_t enb_flagP,
const MBMS_flag_t MBMS_flagP,
const logical_chan_id_t channel_idP,
const tb_size_t tb_sizeP,
char *buffer_pP
#if (LTE_RRC_VERSION >= MAKE_VERSION(14, 0, 0))
,const uint32_t sourceL2Id
,const uint32_t destinationL2Id
#endif
)
{
int ret;
nr_rlc_ue_t *ue;
nr_rlc_entity_t *rb;
int is_enb;
int maxsize;
nr_rlc_manager_lock(nr_rlc_ue_manager);
ue = nr_rlc_manager_get_ue(nr_rlc_ue_manager, rntiP);
switch (channel_idP) {
case 1 ... 2: rb = ue->srb[channel_idP - 1]; break;
case 3 ... 7: rb = ue->drb[channel_idP - 3]; break;
default: rb = NULL; break;
}
if (rb != NULL) {
rb->set_time(rb, nr_rlc_current_time);
/* UE does not seem to use saved_status_ind_tb_size */
is_enb = nr_rlc_manager_get_enb_flag(nr_rlc_ue_manager);
if (is_enb)
maxsize = ue->saved_status_ind_tb_size[channel_idP - 1];
else
maxsize = tb_sizeP;
ret = rb->generate_pdu(rb, buffer_pP, maxsize);
} else {
LOG_E(RLC, "%s:%d:%s: fatal: data req for unknown RB\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
ret = 0;
}
nr_rlc_manager_unlock(nr_rlc_ue_manager);
if (enb_flagP)
T(T_ENB_RLC_MAC_DL, T_INT(module_idP), T_INT(rntiP),
T_INT(channel_idP), T_INT(ret));
return ret;
}
mac_rlc_status_resp_t mac_rlc_status_ind(
const module_id_t module_idP,
const rnti_t rntiP,
const eNB_index_t eNB_index,
const frame_t frameP,
const sub_frame_t subframeP,
const eNB_flag_t enb_flagP,
const MBMS_flag_t MBMS_flagP,
const logical_chan_id_t channel_idP,
const tb_size_t tb_sizeP
#if (LTE_RRC_VERSION >= MAKE_VERSION(14, 0, 0))
,const uint32_t sourceL2Id
,const uint32_t destinationL2Id
#endif
)
{
nr_rlc_ue_t *ue;
mac_rlc_status_resp_t ret;
nr_rlc_entity_t *rb;
/* TODO: handle time a bit more properly */
if (nr_rlc_current_time_last_frame != frameP ||
nr_rlc_current_time_last_subframe != subframeP) {
nr_rlc_current_time++;
nr_rlc_current_time_last_frame = frameP;
nr_rlc_current_time_last_subframe = subframeP;
}
nr_rlc_manager_lock(nr_rlc_ue_manager);
ue = nr_rlc_manager_get_ue(nr_rlc_ue_manager, rntiP);
switch (channel_idP) {
case 1 ... 2: rb = ue->srb[channel_idP - 1]; break;
case 3 ... 7: rb = ue->drb[channel_idP - 3]; break;
default: rb = NULL; break;
}
if (rb != NULL) {
nr_rlc_entity_buffer_status_t buf_stat;
rb->set_time(rb, nr_rlc_current_time);
/* 36.321 deals with BSR values up to 3000000 bytes, after what it
* reports '> 3000000' (table 6.1.3.1-2). Passing 4000000 is thus
* more than enough.
*/
buf_stat = rb->buffer_status(rb, 4000000);
ret.bytes_in_buffer = buf_stat.status_size
+ buf_stat.retx_size
+ buf_stat.tx_size;
ue->saved_status_ind_tb_size[channel_idP - 1] = tb_sizeP;
} else {
ret.bytes_in_buffer = 0;
}
nr_rlc_manager_unlock(nr_rlc_ue_manager);
ret.pdus_in_buffer = 0;
/* TODO: creation time may be important (unit: frame, as it seems) */
ret.head_sdu_creation_time = 0;
ret.head_sdu_remaining_size_to_send = 0;
ret.head_sdu_is_segmented = 0;
return ret;
}
int oai_emulation;
rlc_op_status_t rlc_data_req (const protocol_ctxt_t *const ctxt_pP,
const srb_flag_t srb_flagP,
const MBMS_flag_t MBMS_flagP,
const rb_id_t rb_idP,
const mui_t muiP,
confirm_t confirmP,
sdu_size_t sdu_sizeP,
mem_block_t *sdu_pP
#if (LTE_RRC_VERSION >= MAKE_VERSION(14, 0, 0))
,const uint32_t *const sourceL2Id
,const uint32_t *const destinationL2Id
#endif
)
{
int rnti = ctxt_pP->rnti;
nr_rlc_ue_t *ue;
nr_rlc_entity_t *rb;
LOG_D(RLC, "%s rnti %d srb_flag %d rb_id %d mui %d confirm %d sdu_size %d MBMS_flag %d\n",
__FUNCTION__, rnti, srb_flagP, rb_idP, muiP, confirmP, sdu_sizeP,
MBMS_flagP);
if (ctxt_pP->enb_flag)
T(T_ENB_RLC_DL, T_INT(ctxt_pP->module_id),
T_INT(ctxt_pP->rnti), T_INT(rb_idP), T_INT(sdu_sizeP));
nr_rlc_manager_lock(nr_rlc_ue_manager);
ue = nr_rlc_manager_get_ue(nr_rlc_ue_manager, rnti);
rb = NULL;
if (srb_flagP) {
if (rb_idP >= 1 && rb_idP <= 2)
rb = ue->srb[rb_idP - 1];
} else {
if (rb_idP >= 1 && rb_idP <= 5)
rb = ue->drb[rb_idP - 1];
}
if (rb != NULL) {
rb->set_time(rb, nr_rlc_current_time);
rb->recv_sdu(rb, (char *)sdu_pP->data, sdu_sizeP, muiP);
} else {
LOG_E(RLC, "%s:%d:%s: fatal: SDU sent to unknown RB\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
nr_rlc_manager_unlock(nr_rlc_ue_manager);
free_mem_block(sdu_pP, __func__);
return RLC_OP_STATUS_OK;
}
int rlc_module_init(int enb_flag)
{
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
static int inited = 0;
if (pthread_mutex_lock(&lock)) abort();
if (inited) {
LOG_E(RLC, "%s:%d:%s: fatal\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
inited = 1;
nr_rlc_ue_manager = new_nr_rlc_ue_manager(enb_flag);
if (pthread_mutex_unlock(&lock)) abort();
return 0;
}
void rlc_util_print_hex_octets(comp_name_t componentP, unsigned char *dataP, const signed long sizeP)
{
}
static void deliver_sdu(void *_ue, nr_rlc_entity_t *entity, char *buf, int size)
{
nr_rlc_ue_t *ue = _ue;
int is_srb;
int rb_id;
protocol_ctxt_t ctx;
mem_block_t *memblock;
int i;
int is_enb;
/* is it SRB? */
for (i = 0; i < 2; i++) {
if (entity == ue->srb[i]) {
is_srb = 1;
rb_id = i+1;
goto rb_found;
}
}
/* maybe DRB? */
for (i = 0; i < 5; i++) {
if (entity == ue->drb[i]) {
is_srb = 0;
rb_id = i+1;
goto rb_found;
}
}
LOG_E(RLC, "%s:%d:%s: fatal, no RB found for ue %d\n",
__FILE__, __LINE__, __FUNCTION__, ue->rnti);
exit(1);
rb_found:
LOG_D(RLC, "%s:%d:%s: delivering SDU (rnti %d is_srb %d rb_id %d) size %d",
__FILE__, __LINE__, __FUNCTION__, ue->rnti, is_srb, rb_id, size);
memblock = get_free_mem_block(size, __func__);
if (memblock == NULL) {
LOG_E(RLC, "%s:%d:%s: ERROR: get_free_mem_block failed\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
memcpy(memblock->data, buf, size);
/* unused fields? */
ctx.instance = 0;
ctx.frame = 0;
ctx.subframe = 0;
ctx.eNB_index = 0;
ctx.configured = 1;
ctx.brOption = 0;
/* used fields? */
ctx.module_id = 0;
ctx.rnti = ue->rnti;
is_enb = nr_rlc_manager_get_enb_flag(nr_rlc_ue_manager);
ctx.enb_flag = is_enb;
if (is_enb) {
T(T_ENB_RLC_UL,
T_INT(0 /*ctxt_pP->module_id*/),
T_INT(ue->rnti), T_INT(rb_id), T_INT(size));
}
if (!pdcp_data_ind(&ctx, is_srb, 0, rb_id, size, memblock)) {
LOG_E(RLC, "%s:%d:%s: ERROR: pdcp_data_ind failed\n", __FILE__, __LINE__, __FUNCTION__);
/* what to do in case of failure? for the moment: nothing */
}
}
static void successful_delivery(void *_ue, nr_rlc_entity_t *entity, int sdu_id)
{
nr_rlc_ue_t *ue = _ue;
int i;
int is_srb;
int rb_id;
#if 0
MessageDef *msg;
#endif
int is_enb;
/* is it SRB? */
for (i = 0; i < 2; i++) {
if (entity == ue->srb[i]) {
is_srb = 1;
rb_id = i+1;
goto rb_found;
}
}
/* maybe DRB? */
for (i = 0; i < 5; i++) {
if (entity == ue->drb[i]) {
is_srb = 0;
rb_id = i+1;
goto rb_found;
}
}
LOG_E(RLC, "%s:%d:%s: fatal, no RB found for ue %d\n",
__FILE__, __LINE__, __FUNCTION__, ue->rnti);
exit(1);
rb_found:
LOG_D(RLC, "sdu %d was successfully delivered on %s %d\n",
sdu_id,
is_srb ? "SRB" : "DRB",
rb_id);
/* TODO: do something for DRBs? */
if (is_srb == 0)
return;
is_enb = nr_rlc_manager_get_enb_flag(nr_rlc_ue_manager);
if (!is_enb)
return;
#if 0
msg = itti_alloc_new_message(TASK_RLC_ENB, RLC_SDU_INDICATION);
RLC_SDU_INDICATION(msg).rnti = ue->rnti;
RLC_SDU_INDICATION(msg).is_successful = 1;
RLC_SDU_INDICATION(msg).srb_id = rb_id;
RLC_SDU_INDICATION(msg).message_id = sdu_id;
/* TODO: accept more than 1 instance? here we send to instance id 0 */
itti_send_msg_to_task(TASK_RRC_ENB, 0, msg);
#endif
}
static void max_retx_reached(void *_ue, nr_rlc_entity_t *entity)
{
nr_rlc_ue_t *ue = _ue;
int i;
int is_srb;
int rb_id;
#if 0
MessageDef *msg;
#endif
int is_enb;
/* is it SRB? */
for (i = 0; i < 2; i++) {
if (entity == ue->srb[i]) {
is_srb = 1;
rb_id = i+1;
goto rb_found;
}
}
/* maybe DRB? */
for (i = 0; i < 5; i++) {
if (entity == ue->drb[i]) {
is_srb = 0;
rb_id = i+1;
goto rb_found;
}
}
LOG_E(RLC, "%s:%d:%s: fatal, no RB found for ue %d\n",
__FILE__, __LINE__, __FUNCTION__, ue->rnti);
exit(1);
rb_found:
LOG_D(RLC, "max RETX reached on %s %d\n",
is_srb ? "SRB" : "DRB",
rb_id);
/* TODO: do something for DRBs? */
if (is_srb == 0)
return;
is_enb = nr_rlc_manager_get_enb_flag(nr_rlc_ue_manager);
if (!is_enb)
return;
#if 0
msg = itti_alloc_new_message(TASK_RLC_ENB, RLC_SDU_INDICATION);
RLC_SDU_INDICATION(msg).rnti = ue->rnti;
RLC_SDU_INDICATION(msg).is_successful = 0;
RLC_SDU_INDICATION(msg).srb_id = rb_id;
RLC_SDU_INDICATION(msg).message_id = -1;
/* TODO: accept more than 1 instance? here we send to instance id 0 */
itti_send_msg_to_task(TASK_RRC_ENB, 0, msg);
#endif
}
static void add_srb(int rnti, struct LTE_SRB_ToAddMod *s)
{
nr_rlc_entity_t *nr_rlc_am;
nr_rlc_ue_t *ue;
struct LTE_SRB_ToAddMod__rlc_Config *r = s->rlc_Config;
struct LTE_SRB_ToAddMod__logicalChannelConfig *l = s->logicalChannelConfig;
int srb_id = s->srb_Identity;
int logical_channel_group;
//int t_reordering;
int t_status_prohibit;
int t_poll_retransmit;
int poll_pdu;
int poll_byte;
int max_retx_threshold;
int t_reassembly;
int sn_field_length;
if (srb_id != 1 && srb_id != 2) {
LOG_E(RLC, "%s:%d:%s: fatal, bad srb id %d\n",
__FILE__, __LINE__, __FUNCTION__, srb_id);
exit(1);
}
switch (l->present) {
case LTE_SRB_ToAddMod__logicalChannelConfig_PR_explicitValue:
logical_channel_group = *l->choice.explicitValue.ul_SpecificParameters->logicalChannelGroup;
break;
case LTE_SRB_ToAddMod__logicalChannelConfig_PR_defaultValue:
/* default value from 36.331 9.2.1 */
logical_channel_group = 0;
break;
default:
LOG_E(RLC, "%s:%d:%s: fatal error\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
/* TODO: accept other values? */
if (logical_channel_group != 0) {
LOG_E(RLC, "%s:%d:%s: fatal error\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
switch (r->present) {
case LTE_SRB_ToAddMod__rlc_Config_PR_explicitValue: {
struct LTE_RLC_Config__am *am;
if (r->choice.explicitValue.present != LTE_RLC_Config_PR_am) {
LOG_E(RLC, "%s:%d:%s: fatal error, must be RLC AM\n",
__FILE__, __LINE__, __FUNCTION__);
exit(1);
}
am = &r->choice.explicitValue.choice.am;
//t_reordering = decode_t_reordering(am->dl_AM_RLC.t_Reordering);
t_status_prohibit = decode_t_status_prohibit(am->dl_AM_RLC.t_StatusProhibit);
t_poll_retransmit = decode_t_poll_retransmit(am->ul_AM_RLC.t_PollRetransmit);
poll_pdu = decode_poll_pdu(am->ul_AM_RLC.pollPDU);
poll_byte = decode_poll_byte(am->ul_AM_RLC.pollByte);
max_retx_threshold = decode_max_retx_threshold(am->ul_AM_RLC.maxRetxThreshold);
break;
}
case LTE_SRB_ToAddMod__rlc_Config_PR_defaultValue:
/* default values from 36.331 9.2.1 */
//t_reordering = 35;
t_status_prohibit = 0;
t_poll_retransmit = 45;
poll_pdu = -1;
poll_byte = -1;
max_retx_threshold = 4;
break;
default:
LOG_E(RLC, "%s:%d:%s: fatal error\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
nr_rlc_manager_lock(nr_rlc_ue_manager);
ue = nr_rlc_manager_get_ue(nr_rlc_ue_manager, rnti);
if (ue->srb[srb_id-1] != NULL) {
LOG_D(RLC, "%s:%d:%s: warning SRB %d already exist for ue %d, do nothing\n",
__FILE__, __LINE__, __FUNCTION__, srb_id, rnti);
} else {
/* hack: hardcode values for NR */
t_poll_retransmit = 45;
t_reassembly = 35;
t_status_prohibit = 0;
poll_pdu = -1;
poll_byte = -1;
max_retx_threshold = 8;
sn_field_length = 12;
nr_rlc_am = new_nr_rlc_entity_am(100000,
100000,
deliver_sdu, ue,
successful_delivery, ue,
max_retx_reached, ue,
t_poll_retransmit,
t_reassembly, t_status_prohibit,
poll_pdu, poll_byte, max_retx_threshold,
sn_field_length);
nr_rlc_ue_add_srb_rlc_entity(ue, srb_id, nr_rlc_am);
LOG_D(RLC, "%s:%d:%s: added srb %d to ue %d\n",
__FILE__, __LINE__, __FUNCTION__, srb_id, rnti);
}
nr_rlc_manager_unlock(nr_rlc_ue_manager);
}
static void add_drb_am(int rnti, struct LTE_DRB_ToAddMod *s)
{
nr_rlc_entity_t *nr_rlc_am;
nr_rlc_ue_t *ue;
struct LTE_RLC_Config *r = s->rlc_Config;
struct LTE_LogicalChannelConfig *l = s->logicalChannelConfig;
int drb_id = s->drb_Identity;
int channel_id = *s->logicalChannelIdentity;
int logical_channel_group;
//int t_reordering;
int t_status_prohibit;
int t_poll_retransmit;
int poll_pdu;
int poll_byte;
int max_retx_threshold;
int t_reassembly;
int sn_field_length;
if (!(drb_id >= 1 && drb_id <= 5)) {
LOG_E(RLC, "%s:%d:%s: fatal, bad srb id %d\n",
__FILE__, __LINE__, __FUNCTION__, drb_id);
exit(1);
}
if (channel_id != drb_id + 2) {
LOG_E(RLC, "%s:%d:%s: todo, remove this limitation\n",
__FILE__, __LINE__, __FUNCTION__);
exit(1);
}
logical_channel_group = *l->ul_SpecificParameters->logicalChannelGroup;
/* TODO: accept other values? */
if (logical_channel_group != 1) {
LOG_E(RLC, "%s:%d:%s: fatal error\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
switch (r->present) {
case LTE_RLC_Config_PR_am: {
struct LTE_RLC_Config__am *am;
am = &r->choice.am;
//t_reordering = decode_t_reordering(am->dl_AM_RLC.t_Reordering);
t_status_prohibit = decode_t_status_prohibit(am->dl_AM_RLC.t_StatusProhibit);
t_poll_retransmit = decode_t_poll_retransmit(am->ul_AM_RLC.t_PollRetransmit);
poll_pdu = decode_poll_pdu(am->ul_AM_RLC.pollPDU);
poll_byte = decode_poll_byte(am->ul_AM_RLC.pollByte);
max_retx_threshold = decode_max_retx_threshold(am->ul_AM_RLC.maxRetxThreshold);
break;
}
default:
LOG_E(RLC, "%s:%d:%s: fatal error\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
nr_rlc_manager_lock(nr_rlc_ue_manager);
ue = nr_rlc_manager_get_ue(nr_rlc_ue_manager, rnti);
if (ue->drb[drb_id-1] != NULL) {
LOG_D(RLC, "%s:%d:%s: warning DRB %d already exist for ue %d, do nothing\n",
__FILE__, __LINE__, __FUNCTION__, drb_id, rnti);
} else {
/* hack: hardcode values for NR */
t_poll_retransmit = 45;
t_reassembly = 35;
t_status_prohibit = 0;
poll_pdu = -1;
poll_byte = -1;
max_retx_threshold = 8;
sn_field_length = 12;
nr_rlc_am = new_nr_rlc_entity_am(100000,
100000,
deliver_sdu, ue,
successful_delivery, ue,
max_retx_reached, ue,
t_poll_retransmit,
t_reassembly, t_status_prohibit,
poll_pdu, poll_byte, max_retx_threshold,
sn_field_length);
nr_rlc_ue_add_drb_rlc_entity(ue, drb_id, nr_rlc_am);
LOG_D(RLC, "%s:%d:%s: added drb %d to ue %d\n",
__FILE__, __LINE__, __FUNCTION__, drb_id, rnti);
}
nr_rlc_manager_unlock(nr_rlc_ue_manager);
}
static void add_drb_um(int rnti, struct LTE_DRB_ToAddMod *s)
{
nr_rlc_entity_t *nr_rlc_um;
nr_rlc_ue_t *ue;
struct LTE_RLC_Config *r = s->rlc_Config;
struct LTE_LogicalChannelConfig *l = s->logicalChannelConfig;
int drb_id = s->drb_Identity;
int channel_id = *s->logicalChannelIdentity;
int logical_channel_group;
//int t_reordering;
int sn_field_length;
int t_reassembly;
if (!(drb_id >= 1 && drb_id <= 5)) {
LOG_E(RLC, "%s:%d:%s: fatal, bad srb id %d\n",
__FILE__, __LINE__, __FUNCTION__, drb_id);
exit(1);
}
if (channel_id != drb_id + 2) {
LOG_E(RLC, "%s:%d:%s: todo, remove this limitation\n",
__FILE__, __LINE__, __FUNCTION__);
exit(1);
}
logical_channel_group = *l->ul_SpecificParameters->logicalChannelGroup;
/* TODO: accept other values? */
if (logical_channel_group != 1) {
LOG_E(RLC, "%s:%d:%s: fatal error\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
switch (r->present) {
case LTE_RLC_Config_PR_um_Bi_Directional: {
struct LTE_RLC_Config__um_Bi_Directional *um;
um = &r->choice.um_Bi_Directional;
//t_reordering = decode_t_reordering(um->dl_UM_RLC.t_Reordering);
if (um->dl_UM_RLC.sn_FieldLength != um->ul_UM_RLC.sn_FieldLength) {
LOG_E(RLC, "%s:%d:%s: fatal\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
sn_field_length = decode_sn_field_length(um->dl_UM_RLC.sn_FieldLength);
break;
}
default:
LOG_E(RLC, "%s:%d:%s: fatal error\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
nr_rlc_manager_lock(nr_rlc_ue_manager);
ue = nr_rlc_manager_get_ue(nr_rlc_ue_manager, rnti);
if (ue->drb[drb_id-1] != NULL) {
LOG_D(RLC, "%s:%d:%s: warning DRB %d already exist for ue %d, do nothing\n",
__FILE__, __LINE__, __FUNCTION__, drb_id, rnti);
} else {
/* hack: hardcode values for NR */
t_reassembly = 35;
sn_field_length = 6;
nr_rlc_um = new_nr_rlc_entity_um(1000000,
1000000,
deliver_sdu, ue,
t_reassembly,
sn_field_length);
nr_rlc_ue_add_drb_rlc_entity(ue, drb_id, nr_rlc_um);
LOG_D(RLC, "%s:%d:%s: added drb %d to ue %d\n",
__FILE__, __LINE__, __FUNCTION__, drb_id, rnti);
}
nr_rlc_manager_unlock(nr_rlc_ue_manager);
}
static void add_drb(int rnti, struct LTE_DRB_ToAddMod *s)
{
switch (s->rlc_Config->present) {
case LTE_RLC_Config_PR_am:
add_drb_am(rnti, s);
break;
case LTE_RLC_Config_PR_um_Bi_Directional:
add_drb_um(rnti, s);
break;
default:
LOG_E(RLC, "%s:%d:%s: fatal: unhandled DRB type\n",
__FILE__, __LINE__, __FUNCTION__);
exit(1);
}
}
rlc_op_status_t rrc_rlc_config_asn1_req (const protocol_ctxt_t * const ctxt_pP,
const LTE_SRB_ToAddModList_t * const srb2add_listP,
const LTE_DRB_ToAddModList_t * const drb2add_listP,
const LTE_DRB_ToReleaseList_t * const drb2release_listP
#if (LTE_RRC_VERSION >= MAKE_VERSION(9, 0, 0))
,const LTE_PMCH_InfoList_r9_t * const pmch_InfoList_r9_pP
,const uint32_t sourceL2Id
,const uint32_t destinationL2Id
#endif
)
{
int rnti = ctxt_pP->rnti;
int i;
if (/*ctxt_pP->enb_flag != 1 ||*/ ctxt_pP->module_id != 0 /*||
ctxt_pP->instance != 0 || ctxt_pP->eNB_index != 0 ||
ctxt_pP->configured != 1 || ctxt_pP->brOption != 0 */) {
LOG_E(RLC, "%s: ctxt_pP not handled (%d %d %d %d %d %d)\n", __FUNCTION__,
ctxt_pP->enb_flag , ctxt_pP->module_id, ctxt_pP->instance,
ctxt_pP->eNB_index, ctxt_pP->configured, ctxt_pP->brOption);
exit(1);
}
if (pmch_InfoList_r9_pP != NULL) {
LOG_E(RLC, "%s: pmch_InfoList_r9_pP not handled\n", __FUNCTION__);
exit(1);
}
if (drb2release_listP != NULL) {
LOG_E(RLC, "%s:%d:%s: TODO\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
if (srb2add_listP != NULL) {
for (i = 0; i < srb2add_listP->list.count; i++) {
add_srb(rnti, srb2add_listP->list.array[i]);
}
}
if (drb2add_listP != NULL) {
for (i = 0; i < drb2add_listP->list.count; i++) {
add_drb(rnti, drb2add_listP->list.array[i]);
}
}
return RLC_OP_STATUS_OK;
}
rlc_op_status_t rrc_rlc_config_req (
const protocol_ctxt_t* const ctxt_pP,
const srb_flag_t srb_flagP,
const MBMS_flag_t mbms_flagP,
const config_action_t actionP,
const rb_id_t rb_idP,
const rlc_info_t rlc_infoP)
{
nr_rlc_ue_t *ue;
int i;
if (mbms_flagP) {
LOG_E(RLC, "%s:%d:%s: todo (MBMS NOT supported)\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
if (actionP != CONFIG_ACTION_REMOVE) {
LOG_E(RLC, "%s:%d:%s: todo (only CONFIG_ACTION_REMOVE supported)\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
if (ctxt_pP->module_id) {
LOG_E(RLC, "%s:%d:%s: todo (only module_id 0 supported)\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
if ((srb_flagP && !(rb_idP >= 1 && rb_idP <= 2)) ||
(!srb_flagP && !(rb_idP >= 1 && rb_idP <= 5))) {
LOG_E(RLC, "%s:%d:%s: bad rb_id (%d) (is_srb %d)\n", __FILE__, __LINE__, __FUNCTION__, rb_idP, srb_flagP);
exit(1);
}
nr_rlc_manager_lock(nr_rlc_ue_manager);
LOG_D(RLC, "%s:%d:%s: remove rb %d (is_srb %d) for UE %d\n", __FILE__, __LINE__, __FUNCTION__, rb_idP, srb_flagP, ctxt_pP->rnti);
ue = nr_rlc_manager_get_ue(nr_rlc_ue_manager, ctxt_pP->rnti);
if (srb_flagP) {
if (ue->srb[rb_idP-1] != NULL) {
ue->srb[rb_idP-1]->delete(ue->srb[rb_idP-1]);
ue->srb[rb_idP-1] = NULL;
} else
LOG_W(RLC, "removing non allocated SRB %d, do nothing\n", rb_idP);
} else {
if (ue->drb[rb_idP-1] != NULL) {
ue->drb[rb_idP-1]->delete(ue->drb[rb_idP-1]);
ue->drb[rb_idP-1] = NULL;
} else
LOG_W(RLC, "removing non allocated DRB %d, do nothing\n", rb_idP);
}
/* remove UE if it has no more RB configured */
for (i = 0; i < 2; i++)
if (ue->srb[i] != NULL)
break;
if (i == 2) {
for (i = 0; i < 5; i++)
if (ue->drb[i] != NULL)
break;
if (i == 5)
nr_rlc_manager_remove_ue(nr_rlc_ue_manager, ctxt_pP->rnti);
}
nr_rlc_manager_unlock(nr_rlc_ue_manager);
return RLC_OP_STATUS_OK;
}
void rrc_rlc_register_rrc (rrc_data_ind_cb_t rrc_data_indP, rrc_data_conf_cb_t rrc_data_confP)
{
/* nothing to do */
}
rlc_op_status_t rrc_rlc_remove_ue (const protocol_ctxt_t* const x)
{
LOG_D(RLC, "%s:%d:%s: remove UE %d\n", __FILE__, __LINE__, __FUNCTION__, x->rnti);
nr_rlc_manager_lock(nr_rlc_ue_manager);
nr_rlc_manager_remove_ue(nr_rlc_ue_manager, x->rnti);
nr_rlc_manager_unlock(nr_rlc_ue_manager);
return RLC_OP_STATUS_OK;
}
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#include "nr_rlc_pdu.h"
#include <stdlib.h>
#include <string.h>
#include "LOG/log.h"
/**************************************************************************/
/* RX PDU management */
/**************************************************************************/
nr_rlc_pdu_t *nr_rlc_new_pdu(int sn, int so, int is_first,
int is_last, char *data, int size)
{
nr_rlc_pdu_t *ret = malloc(sizeof(nr_rlc_pdu_t));
if (ret == NULL) {
LOG_E(RLC, "%s:%d:%s: out of memory\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
ret->sn = sn;
ret->so = so;
ret->size = size;
ret->is_first = is_first;
ret->is_last = is_last;
ret->next = NULL;
ret->data = malloc(size);
if (ret->data == NULL) {
LOG_E(RLC, "%s:%d:%s: out of memory\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
memcpy(ret->data, data, size);
return ret;
}
void nr_rlc_free_pdu(nr_rlc_pdu_t *pdu)
{
free(pdu->data);
free(pdu);
}
nr_rlc_pdu_t *nr_rlc_pdu_list_add(
int (*sn_compare)(void *, int, int), void *sn_compare_data,
nr_rlc_pdu_t *list, nr_rlc_pdu_t *pdu)
{
nr_rlc_pdu_t head;
nr_rlc_pdu_t *cur;
nr_rlc_pdu_t *prev;
head.next = list;
cur = list;
prev = &head;
/* order is by 'sn', if 'sn' is the same then order is by 'so' */
while (cur != NULL) {
/* check if 'pdu' is before 'cur' in the list */
if (sn_compare(sn_compare_data, cur->sn, pdu->sn) > 0 ||
(cur->sn == pdu->sn && cur->so > pdu->so)) {
break;
}
prev = cur;
cur = cur->next;
}
prev->next = pdu;
pdu->next = cur;
return head.next;
}
/**************************************************************************/
/* PDU decoder */
/**************************************************************************/
void nr_rlc_pdu_decoder_init(nr_rlc_pdu_decoder_t *decoder,
char *buffer, int size)
{
decoder->error = 0;
decoder->byte = 0;
decoder->bit = 0;
decoder->buffer = buffer;
decoder->size = size;
}
static int get_bit(nr_rlc_pdu_decoder_t *decoder)
{
int ret;
if (decoder->byte >= decoder->size) {
decoder->error = 1;
return 0;
}
ret = (decoder->buffer[decoder->byte] >> (7 - decoder->bit)) & 1;
decoder->bit++;
if (decoder->bit == 8) {
decoder->bit = 0;
decoder->byte++;
}
return ret;
}
int nr_rlc_pdu_decoder_get_bits(nr_rlc_pdu_decoder_t *decoder, int count)
{
int ret = 0;
int i;
if (count > 31) {
LOG_E(RLC, "%s:%d:%s: fatal\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
for (i = 0; i < count; i++) {
ret <<= 1;
ret |= get_bit(decoder);
if (decoder->error) return -1;
}
return ret;
}
/**************************************************************************/
/* PDU encoder */
/**************************************************************************/
void nr_rlc_pdu_encoder_init(nr_rlc_pdu_encoder_t *encoder,
char *buffer, int size)
{
encoder->byte = 0;
encoder->bit = 0;
encoder->buffer = buffer;
encoder->size = size;
}
static void put_bit(nr_rlc_pdu_encoder_t *encoder, int bit)
{
if (encoder->byte == encoder->size) {
LOG_E(RLC, "%s:%d:%s: fatal, buffer full\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
encoder->buffer[encoder->byte] <<= 1;
if (bit)
encoder->buffer[encoder->byte] |= 1;
encoder->bit++;
if (encoder->bit == 8) {
encoder->bit = 0;
encoder->byte++;
}
}
void nr_rlc_pdu_encoder_put_bits(nr_rlc_pdu_encoder_t *encoder,
int value, int count)
{
int i;
int x;
if (count > 31) {
LOG_E(RLC, "%s:%d:%s: fatal\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
x = 1 << (count - 1);
for (i = 0; i < count; i++, x >>= 1)
put_bit(encoder, value & x);
}
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#ifndef _NR_RLC_PDU_H_
#define _NR_RLC_PDU_H_
/**************************************************************************/
/* RX PDU management */
/**************************************************************************/
typedef struct nr_rlc_pdu_t {
int sn;
char *data; /* contains only SDU bytes, no PDU header */
int size; /* size of SDU data, no PDU header bytes counted */
int so;
int is_first;
int is_last;
struct nr_rlc_pdu_t *next;
} nr_rlc_pdu_t;
nr_rlc_pdu_t *nr_rlc_new_pdu(int sn, int so, int is_first,
int is_last, char *data, int size);
void nr_rlc_free_pdu(nr_rlc_pdu_t *pdu);
nr_rlc_pdu_t *nr_rlc_pdu_list_add(
int (*sn_compare)(void *, int, int), void *sn_compare_data,
nr_rlc_pdu_t *list, nr_rlc_pdu_t *pdu);
/**************************************************************************/
/* PDU decoder */
/**************************************************************************/
typedef struct {
int error;
int byte; /* next byte to decode */
int bit; /* next bit in next byte to decode */
char *buffer;
int size;
} nr_rlc_pdu_decoder_t;
void nr_rlc_pdu_decoder_init(nr_rlc_pdu_decoder_t *decoder,
char *buffer, int size);
#define nr_rlc_pdu_decoder_in_error(d) ((d)->error == 1)
int nr_rlc_pdu_decoder_get_bits(nr_rlc_pdu_decoder_t *decoder, int count);
/**************************************************************************/
/* PDU encoder */
/**************************************************************************/
typedef struct {
int byte; /* next byte to encode */
int bit; /* next bit in next byte to encode */
char *buffer;
int size;
} nr_rlc_pdu_encoder_t;
void nr_rlc_pdu_encoder_init(nr_rlc_pdu_encoder_t *encoder,
char *buffer, int size);
void nr_rlc_pdu_encoder_put_bits(nr_rlc_pdu_encoder_t *encoder,
int value, int count);
#endif /* _NR_RLC_PDU_H_ */
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#include "nr_rlc_sdu.h"
#include <stdlib.h>
#include <string.h>
#include "LOG/log.h"
nr_rlc_sdu_segment_t *nr_rlc_new_sdu(
char *buffer, int size,
int upper_layer_id)
{
nr_rlc_sdu_t *sdu = calloc(1, sizeof(nr_rlc_sdu_t));
nr_rlc_sdu_segment_t *ret = calloc(1, sizeof(nr_rlc_sdu_segment_t));
if (sdu == NULL || ret == NULL)
goto oom;
sdu->ref_count = 1;
sdu->sn = -1; /* set later */
sdu->upper_layer_id = upper_layer_id;
sdu->data = malloc(size);
if (sdu->data == NULL)
goto oom;
memcpy(sdu->data, buffer, size);
sdu->size = size;
sdu->retx_count = -1;
ret->sdu = sdu;
ret->size = size;
ret->so = 0;
ret->is_first = 1;
ret->is_last = 1;
return ret;
oom:
LOG_E(RLC, "%s:%d:%s: out of memory\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
void nr_rlc_free_sdu_segment(nr_rlc_sdu_segment_t *sdu)
{
sdu->sdu->ref_count--;
if (sdu->sdu->ref_count == 0) {
free(sdu->sdu->data);
free(sdu->sdu);
}
free(sdu);
}
void nr_rlc_sdu_segment_list_append(nr_rlc_sdu_segment_t **list,
nr_rlc_sdu_segment_t **end,
nr_rlc_sdu_segment_t *sdu)
{
if (*list == NULL) {
*list = sdu;
*end = sdu;
return;
}
(*end)->next = sdu;
*end = sdu;
}
nr_rlc_sdu_segment_t *nr_rlc_sdu_segment_list_add(
int (*sn_compare)(void *, int, int), void *sn_compare_data,
nr_rlc_sdu_segment_t *list, nr_rlc_sdu_segment_t *sdu_segment)
{
nr_rlc_sdu_segment_t head;
nr_rlc_sdu_segment_t *cur;
nr_rlc_sdu_segment_t *prev;
head.next = list;
cur = list;
prev = &head;
/* order is by 'sn', if 'sn' is the same then order is by 'so' */
while (cur != NULL) {
/* check if 'sdu_segment' is before 'cur' in the list */
if (sn_compare(sn_compare_data, cur->sdu->sn, sdu_segment->sdu->sn) > 0 ||
(cur->sdu->sn == sdu_segment->sdu->sn && cur->so > sdu_segment->so)) {
break;
}
prev = cur;
cur = cur->next;
}
prev->next = sdu_segment;
sdu_segment->next = cur;
return head.next;
}
void nr_rlc_free_sdu_segment_list(nr_rlc_sdu_segment_t *l)
{
nr_rlc_sdu_segment_t *cur;
while (l != NULL) {
cur = l;
l = l->next;
nr_rlc_free_sdu_segment(cur);
}
}
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#ifndef _NR_RLC_SDU_H_
#define _NR_RLC_SDU_H_
typedef struct nr_rlc_sdu_t {
int sn;
int upper_layer_id;
char *data;
int size;
int retx_count;
int ref_count; /* incremented each time the SDU is segmented */
} nr_rlc_sdu_t;
typedef struct nr_rlc_sdu_segment_t {
nr_rlc_sdu_t *sdu;
int size;
int so;
int is_first;
int is_last;
struct nr_rlc_sdu_segment_t *next;
} nr_rlc_sdu_segment_t;
nr_rlc_sdu_segment_t *nr_rlc_new_sdu(
char *buffer, int size,
int upper_layer_id);
void nr_rlc_free_sdu_segment(nr_rlc_sdu_segment_t *sdu);
void nr_rlc_sdu_segment_list_append(nr_rlc_sdu_segment_t **list,
nr_rlc_sdu_segment_t **end,
nr_rlc_sdu_segment_t *sdu);
nr_rlc_sdu_segment_t *nr_rlc_sdu_segment_list_add(
int (*sn_compare)(void *, int, int), void *sn_compare_data,
nr_rlc_sdu_segment_t *list, nr_rlc_sdu_segment_t *sdu_segment);
void nr_rlc_free_sdu_segment_list(nr_rlc_sdu_segment_t *l);
#endif /* _NR_RLC_SDU_H_ */
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#include "nr_rlc_ue_manager.h"
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include "LOG/log.h"
typedef struct {
pthread_mutex_t lock;
nr_rlc_ue_t **ue_list;
int ue_count;
int enb_flag;
} nr_rlc_ue_manager_internal_t;
nr_rlc_ue_manager_t *new_nr_rlc_ue_manager(int enb_flag)
{
nr_rlc_ue_manager_internal_t *ret;
ret = calloc(1, sizeof(nr_rlc_ue_manager_internal_t));
if (ret == NULL) {
LOG_E(RLC, "%s:%d:%s: out of memory\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
if (pthread_mutex_init(&ret->lock, NULL)) abort();
ret->enb_flag = enb_flag;
return ret;
}
int nr_rlc_manager_get_enb_flag(nr_rlc_ue_manager_t *_m)
{
nr_rlc_ue_manager_internal_t *m = _m;
return m->enb_flag;
}
void nr_rlc_manager_lock(nr_rlc_ue_manager_t *_m)
{
nr_rlc_ue_manager_internal_t *m = _m;
if (pthread_mutex_lock(&m->lock)) {
LOG_E(RLC, "%s:%d:%s: fatal\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
}
void nr_rlc_manager_unlock(nr_rlc_ue_manager_t *_m)
{
nr_rlc_ue_manager_internal_t *m = _m;
if (pthread_mutex_unlock(&m->lock)) {
LOG_E(RLC, "%s:%d:%s: fatal\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
}
/* must be called with lock acquired */
nr_rlc_ue_t *nr_rlc_manager_get_ue(nr_rlc_ue_manager_t *_m, int rnti)
{
/* TODO: optimze */
nr_rlc_ue_manager_internal_t *m = _m;
int i;
for (i = 0; i < m->ue_count; i++)
if (m->ue_list[i]->rnti == rnti)
return m->ue_list[i];
LOG_D(RLC, "%s:%d:%s: new UE %d\n", __FILE__, __LINE__, __FUNCTION__, rnti);
m->ue_count++;
m->ue_list = realloc(m->ue_list, sizeof(nr_rlc_ue_t *) * m->ue_count);
if (m->ue_list == NULL) {
LOG_E(RLC, "%s:%d:%s: out of memory\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
m->ue_list[m->ue_count-1] = calloc(1, sizeof(nr_rlc_ue_t));
if (m->ue_list[m->ue_count-1] == NULL) {
LOG_E(RLC, "%s:%d:%s: out of memory\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
m->ue_list[m->ue_count-1]->rnti = rnti;
return m->ue_list[m->ue_count-1];
}
/* must be called with lock acquired */
void nr_rlc_manager_remove_ue(nr_rlc_ue_manager_t *_m, int rnti)
{
nr_rlc_ue_manager_internal_t *m = _m;
nr_rlc_ue_t *ue;
int i;
int j;
for (i = 0; i < m->ue_count; i++)
if (m->ue_list[i]->rnti == rnti)
break;
if (i == m->ue_count) {
LOG_D(RLC, "%s:%d:%s: warning: ue %d not found\n",
__FILE__, __LINE__, __FUNCTION__,
rnti);
return;
}
ue = m->ue_list[i];
for (j = 0; j < 2; j++)
if (ue->srb[j] != NULL)
ue->srb[j]->delete(ue->srb[j]);
for (j = 0; j < 5; j++)
if (ue->drb[j] != NULL)
ue->drb[j]->delete(ue->drb[j]);
free(ue);
m->ue_count--;
if (m->ue_count == 0) {
free(m->ue_list);
m->ue_list = NULL;
return;
}
memmove(&m->ue_list[i], &m->ue_list[i+1],
(m->ue_count - i) * sizeof(nr_rlc_ue_t *));
m->ue_list = realloc(m->ue_list, m->ue_count * sizeof(nr_rlc_ue_t *));
if (m->ue_list == NULL) {
LOG_E(RLC, "%s:%d:%s: fatal\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
}
/* must be called with lock acquired */
void nr_rlc_ue_add_srb_rlc_entity(nr_rlc_ue_t *ue, int srb_id, nr_rlc_entity_t *entity)
{
if (srb_id < 1 || srb_id > 2) {
LOG_E(RLC, "%s:%d:%s: fatal, bad srb id\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
srb_id--;
if (ue->srb[srb_id] != NULL) {
LOG_E(RLC, "%s:%d:%s: fatal, srb already present\n",
__FILE__, __LINE__, __FUNCTION__);
exit(1);
}
ue->srb[srb_id] = entity;
}
/* must be called with lock acquired */
void nr_rlc_ue_add_drb_rlc_entity(nr_rlc_ue_t *ue, int drb_id, nr_rlc_entity_t *entity)
{
if (drb_id < 1 || drb_id > 5) {
LOG_E(RLC, "%s:%d:%s: fatal, bad drb id\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
drb_id--;
if (ue->drb[drb_id] != NULL) {
LOG_E(RLC, "%s:%d:%s: fatal, drb already present\n",
__FILE__, __LINE__, __FUNCTION__);
exit(1);
}
ue->drb[drb_id] = entity;
}
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#ifndef _NR_RLC_UE_MANAGER_H_
#define _NR_RLC_UE_MANAGER_H_
#include "nr_rlc_entity.h"
typedef void nr_rlc_ue_manager_t;
typedef struct nr_rlc_ue_t {
int rnti;
/* due to openair calling status_ind/data_req, we need to keep this.
* To be considered 'hackish'.
*/
int saved_status_ind_tb_size[2+5];
nr_rlc_entity_t *srb[2];
nr_rlc_entity_t *drb[5];
} nr_rlc_ue_t;
/***********************************************************************/
/* manager functions */
/***********************************************************************/
nr_rlc_ue_manager_t *new_nr_rlc_ue_manager(int enb_flag);
int nr_rlc_manager_get_enb_flag(nr_rlc_ue_manager_t *m);
void nr_rlc_manager_lock(nr_rlc_ue_manager_t *m);
void nr_rlc_manager_unlock(nr_rlc_ue_manager_t *m);
nr_rlc_ue_t *nr_rlc_manager_get_ue(nr_rlc_ue_manager_t *m, int rnti);
void nr_rlc_manager_remove_ue(nr_rlc_ue_manager_t *m, int rnti);
/***********************************************************************/
/* ue functions */
/***********************************************************************/
void nr_rlc_ue_add_srb_rlc_entity(nr_rlc_ue_t *ue, int srb_id, nr_rlc_entity_t *entity);
void nr_rlc_ue_add_drb_rlc_entity(nr_rlc_ue_t *ue, int drb_id, nr_rlc_entity_t *entity);
#endif /* _NR_RLC_UE_MANAGER_H_ */
#include "nr_rlc_entity.h"
#include <stdio.h>
int main(void)
{
char out[32768];
nr_rlc_entity_t *am;
int sdu_id = 0;
int size;
int i;
am = new_nr_rlc_entity_am(100000, 100000,
0, 0,
0, 0,
0, 0,
45, 35, 0,
-1, -1, 8,
12);
char data[8] = { 1, 2, 3, 4, 8, 7, 6, 5 };
am->recv_sdu(am, data, sizeof(data), sdu_id++);
size = am->generate_pdu(am, out, 32768);
printf("generate_pdu[%d]:", size);
for (i = 0; i < size; i++) printf(" %2.2x", (unsigned char)out[i]);
printf("\n");
return 0;
}
#ifndef _LOG_H_
#define _LOG_H_
#include <stdio.h>
#define LOG_E(x, ...) printf(__VA_ARGS__)
#define LOG_D(x, ...) printf(__VA_ARGS__)
#define LOG_W(x, ...) printf(__VA_ARGS__)
#endif /* _LOG_H_ */
CC=gcc
CFLAGS=-Wall -g --coverage -I.
LIB=nr_rlc_entity.o nr_rlc_entity_am.o nr_rlc_entity_um.o nr_rlc_entity_tm.o \
nr_rlc_pdu.o nr_rlc_sdu.o
tests:
@./run_tests.sh
all: clean_run $(TEST).run
%.run: $(TEST).bin
#valgrind ./$(TEST).bin > $(TEST).run_pre 2> $(TEST).valgrind
./$(TEST).bin > $(TEST).run_pre
grep ^TEST $(TEST).run_pre > $(TEST).run
gunzip -c $(TEST).txt.gz > $(TEST).txt
diff -q $(TEST).txt $(TEST).run
$(TEST).bin: $(TEST).o $(LIB)
$(CC) $(CFLAGS) -o $@ $^
%.o: ../%.c
$(CC) $(CFLAGS) -I.. -c -o $@ $<
$(TEST).o: test.c
$(CC) $(CFLAGS) -c -o $@ $< -DTEST='"$(TEST).h"'
clean_run:
rm -f $(TEST).run $(TEST).bin $(TEST).o
clean:
rm -f *.o *.bin *.run *.run_pre *.gcov *.gcda *.gcno test*.txt a.out \
*.valgrind
#!/bin/sh
test_count=13
for i in `seq $test_count`
do
make all TEST=test$i >/dev/null 2>/dev/null
if [ $? != 0 ]
then
echo TEST $i FAILURE
fi
done
#include "../nr_rlc_entity.h"
#include "../nr_rlc_entity_am.h"
#include "../nr_rlc_entity_um.h"
#include "../nr_rlc_entity_tm.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <sys/wait.h>
#include <unistd.h>
/*
* GNB_AM <rx_maxsize> <tx_maxsize> <t_poll_retransmit> <t_reassembly>
* <t_status_prohibit> <poll_pdu> <poll_byte> <max_retx_threshold>
* <sn_field_length>
* create the gNB RLC AM entity with given parameters
*
* UE_AM <rx_maxsize> <tx_maxsize> <t_poll_retransmit> <t_reassembly>
* <t_status_prohibit> <poll_pdu> <poll_byte> <max_retx_threshold>
* <sn_field_length>
* create the UE RLC AM entity with given parameters
*
* GNB_UM <rx_maxsize> <tx_maxsize> <t_reassembly> <sn_field_length>
* create the eNB RLC UM entity with given parameters
*
* UE_UM <rx_maxsize> <tx_maxsize> <t_reassembly> <sn_field_length>
* create the UE RLC UM entity with given parameters
*
* GNB_TM <tx_maxsize>
* create the eNB RLC TM entity with given parameters
*
* UE_UM <tx_maxsize>
* create the UE RLC TM entity with given parameters
*
* TIME <time>
* following actions to be performed at time <time>
* <time> starts at 1
* You must end your test definition with a line 'TIME, -1'.
*
* GNB_SDU <id> <size>
* send an SDU to eNB with id <i> and size <size>
* the SDU is [00 01 ... ff 01 ...]
* (ie. start byte is 00 then we increment for each byte, loop if needed)
*
* UE_SDU <id> <size>
* same as GNB_SDU but the SDU is sent to the UE
*
* GNB_PDU <size> <'size' bytes>
* send a custom PDU from eNB to UE (eNB does not see this PDU at all)
*
* UE_PDU <size> <'size' bytes>
* send a custom PDU from UE to eNB (UE does not see this PDU at all)
*
* GNB_PDU_SIZE <size>
* set 'gnb_pdu_size'
*
* UE_PDU_SIZE <size>
* set 'ue_pdu_size'
*
* GNB_RECV_FAILS <fails>
* set the 'gnb_recv_fails' flag to <fails>
* (1: recv will fail, 0: recv will succeed)
*
* UE_RECV_FAILS <fails>
* same as GNB_RECV_FAILS but for 'ue_recv_fails'
*
* MUST_FAIL
* to be used as first command after the first TIME to indicate
* that the test must fail (ie. exit with non zero, crash not allowed)
*
* GNB_BUFFER_STATUS
* call buffer_status for eNB and print result
*
* UE_BUFFER_STATUS
* call buffer_status for UE and print result
*
* GNB_DISCARD_SDU <sdu ID>
* discards given SDU
*
* UE_DISCARD_SDU <sdu ID>
* discards given SDU
*
* RE_ESTABLISH
* re-establish both eNB and UE
*/
enum action {
GNB_AM, UE_AM,
GNB_UM, UE_UM,
GNB_TM, UE_TM,
TIME, GNB_SDU, UE_SDU, GNB_PDU, UE_PDU,
GNB_PDU_SIZE, UE_PDU_SIZE,
GNB_RECV_FAILS, UE_RECV_FAILS,
MUST_FAIL,
GNB_BUFFER_STATUS, UE_BUFFER_STATUS,
GNB_DISCARD_SDU, UE_DISCARD_SDU,
RE_ESTABLISH
};
int test[] = {
/* TEST is defined at compilation time */
#include TEST
};
void deliver_sdu_gnb_am(void *deliver_sdu_data,
struct nr_rlc_entity_t *_entity,
char *buf, int size)
{
nr_rlc_entity_am_t *entity = (nr_rlc_entity_am_t *)_entity;
printf("TEST: GNB: %"PRIu64": deliver SDU size %d [",
entity->t_current, size);
for (int i = 0; i < size; i++) printf(" %2.2x", (unsigned char)buf[i]);
printf("]\n");
}
void deliver_sdu_gnb_um(void *deliver_sdu_data,
struct nr_rlc_entity_t *_entity,
char *buf, int size)
{
nr_rlc_entity_um_t *entity = (nr_rlc_entity_um_t *)_entity;
printf("TEST: GNB: %"PRIu64": deliver SDU size %d [",
entity->t_current, size);
for (int i = 0; i < size; i++) printf(" %2.2x", (unsigned char)buf[i]);
printf("]\n");
}
void deliver_sdu_gnb_tm(void *deliver_sdu_data,
struct nr_rlc_entity_t *_entity,
char *buf, int size)
{
nr_rlc_entity_tm_t *entity = (nr_rlc_entity_tm_t *)_entity;
printf("TEST: GNB: %"PRIu64": deliver SDU size %d [",
entity->t_current, size);
for (int i = 0; i < size; i++) printf(" %2.2x", (unsigned char)buf[i]);
printf("]\n");
}
void successful_delivery_gnb(void *successful_delivery_data,
nr_rlc_entity_t *_entity, int sdu_id)
{
nr_rlc_entity_am_t *entity = (nr_rlc_entity_am_t *)_entity;
printf("TEST: GNB: %"PRIu64": SDU %d was successfully delivered.\n",
entity->t_current, sdu_id);
}
void max_retx_reached_gnb(void *max_retx_reached_data,
nr_rlc_entity_t *_entity)
{
nr_rlc_entity_am_t *entity = (nr_rlc_entity_am_t *)_entity;
printf("TEST: GNB: %"PRIu64": max RETX reached! radio link failure!\n",
entity->t_current);
exit(1);
}
void deliver_sdu_ue_am(void *deliver_sdu_data,
struct nr_rlc_entity_t *_entity,
char *buf, int size)
{
nr_rlc_entity_am_t *entity = (nr_rlc_entity_am_t *)_entity;
printf("TEST: UE: %"PRIu64": deliver SDU size %d [",
entity->t_current, size);
for (int i = 0; i < size; i++) printf(" %2.2x", (unsigned char)buf[i]);
printf("]\n");
}
void deliver_sdu_ue_um(void *deliver_sdu_data,
struct nr_rlc_entity_t *_entity,
char *buf, int size)
{
nr_rlc_entity_um_t *entity = (nr_rlc_entity_um_t *)_entity;
printf("TEST: UE: %"PRIu64": deliver SDU size %d [",
entity->t_current, size);
for (int i = 0; i < size; i++) printf(" %2.2x", (unsigned char)buf[i]);
printf("]\n");
}
void deliver_sdu_ue_tm(void *deliver_sdu_data,
struct nr_rlc_entity_t *_entity,
char *buf, int size)
{
nr_rlc_entity_tm_t *entity = (nr_rlc_entity_tm_t *)_entity;
printf("TEST: UE: %"PRIu64": deliver SDU size %d [",
entity->t_current, size);
for (int i = 0; i < size; i++) printf(" %2.2x", (unsigned char)buf[i]);
printf("]\n");
}
void successful_delivery_ue(void *successful_delivery_data,
nr_rlc_entity_t *_entity, int sdu_id)
{
nr_rlc_entity_am_t *entity = (nr_rlc_entity_am_t *)_entity;
printf("TEST: UE: %"PRIu64": SDU %d was successfully delivered.\n",
entity->t_current, sdu_id);
}
void max_retx_reached_ue(void *max_retx_reached_data,
nr_rlc_entity_t *_entity)
{
nr_rlc_entity_am_t *entity = (nr_rlc_entity_am_t *)_entity;
printf("TEST: UE: %"PRIu64", max RETX reached! radio link failure!\n",
entity->t_current);
exit(1);
}
int test_main(void)
{
nr_rlc_entity_t *gnb = NULL;
nr_rlc_entity_t *ue = NULL;
int i;
int k;
char *sdu;
char *pdu;
nr_rlc_entity_buffer_status_t buffer_status;
int gnb_do_buffer_status = 0;
int ue_do_buffer_status = 0;
int size;
int pos;
int next_byte_gnb = 0;
int next_byte_ue = 0;
int gnb_recv_fails = 0;
int ue_recv_fails = 0;
int gnb_pdu_size = 1000;
int ue_pdu_size = 1000;
printf("TEST: start\n");
sdu = malloc(16001);
pdu = malloc(3000);
if (sdu == NULL || pdu == NULL) {
printf("out of memory\n");
exit(1);
}
for (i = 0; i < 16001; i++)
sdu[i] = i & 255;
pos = 0;
if (test[pos] != TIME) {
printf("%s:%d:%s: fatal\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
for (i = 1; i < 1000; i++) {
if (i == test[pos+1]) {
pos += 2;
while (test[pos] != TIME)
switch (test[pos]) {
default: printf("fatal: unknown action\n"); exit(1);
case GNB_AM:
gnb = new_nr_rlc_entity_am(test[pos+1], test[pos+2],
deliver_sdu_gnb_am, NULL,
successful_delivery_gnb, NULL,
max_retx_reached_gnb, NULL,
test[pos+3], test[pos+4], test[pos+5],
test[pos+6], test[pos+7], test[pos+8],
test[pos+9]);
pos += 10;
break;
case UE_AM:
ue = new_nr_rlc_entity_am(test[pos+1], test[pos+2],
deliver_sdu_ue_am, NULL,
successful_delivery_ue, NULL,
max_retx_reached_ue, NULL,
test[pos+3], test[pos+4], test[pos+5],
test[pos+6], test[pos+7], test[pos+8],
test[pos+9]);
pos += 10;
break;
case GNB_UM:
gnb = new_nr_rlc_entity_um(test[pos+1], test[pos+2],
deliver_sdu_gnb_um, NULL,
test[pos+3], test[pos+4]);
pos += 5;
break;
case UE_UM:
ue = new_nr_rlc_entity_um(test[pos+1], test[pos+2],
deliver_sdu_ue_um, NULL,
test[pos+3], test[pos+4]);
pos += 5;
break;
case GNB_TM:
gnb = new_nr_rlc_entity_tm(test[pos+1], deliver_sdu_gnb_tm, NULL);
pos += 2;
break;
case UE_TM:
ue = new_nr_rlc_entity_tm(test[pos+1], deliver_sdu_ue_tm, NULL);
pos += 2;
break;
case GNB_SDU:
for (k = 0; k < test[pos+2]; k++, next_byte_gnb++)
sdu[k] = next_byte_gnb;
printf("TEST: GNB: %d: recv_sdu (id %d): size %d: [",
i, test[pos+1], test[pos+2]);
for (k = 0; k < test[pos+2]; k++)
printf(" %2.2x", (unsigned char)sdu[k]);
printf("]\n");
gnb->recv_sdu(gnb, sdu, test[pos+2], test[pos+1]);
pos += 3;
break;
case UE_SDU:
for (k = 0; k < test[pos+2]; k++, next_byte_ue++)
sdu[k] = next_byte_ue;
printf("TEST: UE: %d: recv_sdu (id %d): size %d: [",
i, test[pos+1], test[pos+2]);
for (k = 0; k < test[pos+2]; k++)
printf(" %2.2x", (unsigned char)sdu[k]);
printf("]\n");
ue->recv_sdu(ue, sdu, test[pos+2], test[pos+1]);
pos += 3;
break;
case GNB_PDU:
for (k = 0; k < test[pos+1]; k++)
pdu[k] = test[pos+2+k];
printf("TEST: GNB: %d: custom PDU: size %d: [", i, test[pos+1]);
for (k = 0; k < test[pos+1]; k++) printf(" %2.2x", (unsigned char)pdu[k]);
printf("]\n");
if (!ue_recv_fails)
ue->recv_pdu(ue, pdu, test[pos+1]);
pos += 2 + test[pos+1];
break;
case UE_PDU:
for (k = 0; k < test[pos+1]; k++)
pdu[k] = test[pos+2+k];
printf("TEST: UE: %d: custom PDU: size %d: [", i, test[pos+1]);
for (k = 0; k < test[pos+1]; k++) printf(" %2.2x", (unsigned char)pdu[k]);
printf("]\n");
if (!gnb_recv_fails)
gnb->recv_pdu(gnb, pdu, test[pos+1]);
pos += 2 + test[pos+1];
break;
case GNB_PDU_SIZE:
gnb_pdu_size = test[pos+1];
pos += 2;
break;
case UE_PDU_SIZE:
ue_pdu_size = test[pos+1];
pos += 2;
break;
case GNB_RECV_FAILS:
gnb_recv_fails = test[pos+1];
pos += 2;
break;
case UE_RECV_FAILS:
ue_recv_fails = test[pos+1];
pos += 2;
break;
case MUST_FAIL:
/* do nothing, only used by caller */
pos++;
break;
case GNB_BUFFER_STATUS:
gnb_do_buffer_status = 1;
pos++;
break;
case UE_BUFFER_STATUS:
ue_do_buffer_status = 1;
pos++;
break;
case GNB_DISCARD_SDU:
printf("TEST: GNB: %d: discard SDU %d\n", i, test[pos+1]);
gnb->discard_sdu(gnb, test[pos+1]);
pos += 2;
break;
case UE_DISCARD_SDU:
printf("TEST: UE: %d: discard SDU %d\n", i, test[pos+1]);
ue->discard_sdu(ue, test[pos+1]);
pos += 2;
break;
case RE_ESTABLISH:
printf("TEST: %d: re-establish eNB and UE\n", i);
gnb->reestablishment(gnb);
ue->reestablishment(ue);
pos++;
break;
}
}
gnb->set_time(gnb, i);
ue->set_time(ue, i);
if (gnb_do_buffer_status) {
gnb_do_buffer_status = 0;
buffer_status = gnb->buffer_status(gnb, gnb_pdu_size);
printf("TEST: GNB: %d: buffer_status: status_size %d tx_size %d retx_size %d\n",
i,
buffer_status.status_size,
buffer_status.tx_size,
buffer_status.retx_size);
}
size = gnb->generate_pdu(gnb, pdu, gnb_pdu_size);
if (size) {
printf("TEST: GNB: %d: generate_pdu: size %d: [", i, size);
for (k = 0; k < size; k++) printf(" %2.2x", (unsigned char)pdu[k]);
printf("]\n");
if (!ue_recv_fails)
ue->recv_pdu(ue, pdu, size);
}
if (ue_do_buffer_status) {
ue_do_buffer_status = 0;
buffer_status = ue->buffer_status(ue, ue_pdu_size);
printf("TEST: UE: %d: buffer_status: status_size %d tx_size %d retx_size %d\n",
i,
buffer_status.status_size,
buffer_status.tx_size,
buffer_status.retx_size);
}
size = ue->generate_pdu(ue, pdu, ue_pdu_size);
if (size) {
printf("TEST: UE: %d: generate_pdu: size %d: [", i, size);
for (k = 0; k < size; k++) printf(" %2.2x", (unsigned char)pdu[k]);
printf("]\n");
if (!gnb_recv_fails)
gnb->recv_pdu(gnb, pdu, size);
}
}
gnb->delete(gnb);
ue->delete(ue);
free(sdu);
free(pdu);
return 0;
}
void usage(void)
{
printf("options:\n");
printf(" -no-fork\n");
printf(" don't fork (to ease debugging with gdb)\n");
exit(0);
}
int main(int n, char **v)
{
int must_fail = 0;
int son;
int status;
int i;
int no_fork = 0;
for (i = 1; i < n; i++) {
if (!strcmp(v[i], "-no-fork")) { no_fork = 1; continue; }
usage();
}
if (test[2] == MUST_FAIL)
must_fail = 1;
if (no_fork) return test_main();
son = fork();
if (son == -1) {
perror("fork");
return 1;
}
if (son == 0)
return test_main();
if (wait(&status) == -1) {
perror("wait");
return 1;
}
/* child must quit properly */
if (!WIFEXITED(status))
return 1;
/* child must fail if expected to */
if (must_fail && WEXITSTATUS(status) == 0)
return 1;
/* child must not fail if not expected to */
if (!must_fail && WEXITSTATUS(status))
return 1;
return 0;
}
/*
* basic am test (SN field size 12):
* at time 1, gNB receives an SDU of 10 bytes
* at time 10, UE receives an SDU of 5 bytes
*/
TIME, 1,
GNB_AM, 100000, 100000, 45, 35, 0, -1, -1, 8, 12,
UE_AM, 100000, 100000, 45, 35, 0, -1, -1, 8, 12,
GNB_SDU, 0, 10,
UE_BUFFER_STATUS,
TIME, 10,
UE_SDU, 0, 5,
TIME, -1
/*
* um test (SN field size 6):
* test reassembly
* at time 1, gNB receives an SDU of 50 bytes
* sends it in 3 parts
*/
TIME, 1,
GNB_UM, 100000, 100000, 35, 6,
UE_UM, 100000, 100000, 35, 6,
GNB_PDU_SIZE, 22,
GNB_SDU, 0, 50,
TIME, -1
/*
* um test (SN field size 12):
* test reassembly
* at time 1, gNB receives an SDU of 50 bytes
* sends it in 3 parts
*/
TIME, 1,
GNB_UM, 100000, 100000, 35, 12,
UE_UM, 100000, 100000, 35, 12,
GNB_PDU_SIZE, 22,
GNB_SDU, 0, 50,
TIME, -1
/*
* um test (SN field size 6):
* test reassembly and timeout
* at time 1, gNB receives an SDU of 50 bytes
* sends it in 3 parts
* part 2 not received
*/
TIME, 1,
GNB_UM, 100000, 100000, 35, 6,
UE_UM, 100000, 100000, 35, 6,
GNB_PDU_SIZE, 22,
GNB_SDU, 0, 50,
TIME, 2,
UE_RECV_FAILS, 1,
TIME, 3,
UE_RECV_FAILS, 0,
TIME, -1
/*
* um test (SN field size 12):
* test reassembly and timeout
* at time 1, gNB receives an SDU of 50 bytes
* sends it in 3 parts
* part 2 not received
*/
TIME, 1,
GNB_UM, 100000, 100000, 35, 12,
UE_UM, 100000, 100000, 35, 12,
GNB_PDU_SIZE, 22,
GNB_SDU, 0, 50,
TIME, 2,
UE_RECV_FAILS, 1,
TIME, 3,
UE_RECV_FAILS, 0,
TIME, -1
/*
* basic am test (SN field size 18):
* at time 1, gNB receives an SDU of 10 bytes
* at time 10, UE receives an SDU of 5 bytes
*/
TIME, 1,
GNB_AM, 100000, 100000, 45, 35, 0, -1, -1, 8, 18,
UE_AM, 100000, 100000, 45, 35, 0, -1, -1, 8, 18,
GNB_SDU, 0, 10,
UE_BUFFER_STATUS,
TIME, 10,
UE_SDU, 0, 5,
TIME, -1
/*
* basic um test (SN field size 6):
* at time 1, gNB receives an SDU of 10 bytes
* at time 10, UE receives an SDU of 5 bytes
*/
TIME, 1,
GNB_UM, 100000, 100000, 35, 6,
UE_UM, 100000, 100000, 35, 6,
GNB_SDU, 0, 10,
UE_BUFFER_STATUS,
TIME, 10,
UE_SDU, 0, 5,
TIME, -1
/*
* basic um test (SN field size 12):
* at time 1, gNB receives an SDU of 10 bytes
* at time 10, UE receives an SDU of 5 bytes
*/
TIME, 1,
GNB_UM, 100000, 100000, 35, 6,
UE_UM, 100000, 100000, 35, 6,
GNB_SDU, 0, 10,
UE_BUFFER_STATUS,
TIME, 10,
UE_SDU, 0, 5,
TIME, -1
/*
* basic am test (SN field size 11, not exiting, test must fail):
* at time 1, gNB receives an SDU of 10 bytes
* at time 10, UE receives an SDU of 5 bytes
*/
TIME, 1,
MUST_FAIL,
GNB_AM, 100000, 100000, 45, 35, 0, -1, -1, 8, 11,
UE_AM, 100000, 100000, 45, 35, 0, -1, -1, 8, 11,
GNB_SDU, 0, 10,
UE_BUFFER_STATUS,
TIME, 10,
UE_SDU, 0, 5,
TIME, -1
/*
* basic um test (SN field size 19, bad size, test must fail):
* at time 1, gNB receives an SDU of 10 bytes
* at time 10, UE receives an SDU of 5 bytes
*/
TIME, 1,
MUST_FAIL,
GNB_UM, 100000, 100000, 35, 19,
UE_UM, 100000, 100000, 35, 19,
GNB_SDU, 0, 10,
UE_BUFFER_STATUS,
TIME, 10,
UE_SDU, 0, 5,
TIME, -1
/*
* basic tm test:
* at time 1, gNB receives an SDU of 10 bytes
* at time 10, UE receives an SDU of 5 bytes
*/
TIME, 1,
GNB_TM, 100000,
UE_TM, 100000,
GNB_SDU, 0, 10,
UE_BUFFER_STATUS,
TIME, 10,
UE_SDU, 0, 5,
TIME, -1
/*
* am test (SN field size 12):
* test reassembly
* at time 1, gNB receives an SDU of 50 bytes
* sends 1st part, received by UE
* then sends 2nd part, not received
* then sends 3rd part, received
* then UE receives all
*/
TIME, 1,
GNB_AM, 100000, 100000, 45, 35, 0, -1, -1, 8, 12,
UE_AM, 100000, 100000, 45, 35, 0, -1, -1, 8, 12,
GNB_PDU_SIZE, 22,
GNB_SDU, 0, 50,
TIME, 2,
UE_RECV_FAILS, 1,
TIME, 3,
UE_RECV_FAILS, 0,
TIME, -1
/*
* am test (SN field size 18):
* test reassembly
* at time 1, gNB receives an SDU of 50 bytes
* sends 1st part, received by UE
* then sends 2nd part, not received
* then sends 3rd part, received
* then UE receives all
*/
TIME, 1,
GNB_AM, 100000, 100000, 45, 35, 0, -1, -1, 8, 18,
UE_AM, 100000, 100000, 45, 35, 0, -1, -1, 8, 18,
GNB_PDU_SIZE, 22,
GNB_SDU, 0, 50,
TIME, 2,
UE_RECV_FAILS, 1,
TIME, 3,
UE_RECV_FAILS, 0,
TIME, -1
......@@ -226,7 +226,7 @@ int main(int argc, char **argv)
mac_xface = malloc(sizeof(MAC_xface));
Mac_rlc_xface = (MAC_RLC_XFACE*)malloc16(sizeof(MAC_RLC_XFACE));
rlc_module_init ();
rlc_module_init (1);
pdcp_module_init();
logInit();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment