From 1a46b66d7478626a441bcb970f045b7206a47469 Mon Sep 17 00:00:00 2001 From: Cedric Roux <cedric.roux@eurecom.fr> Date: Fri, 22 Nov 2019 15:10:27 +0100 Subject: [PATCH] nr rlc: first commit Initial implementation. nr_rlc/nr_rlc_oai_api.c will need some clean rewrite at some point. More testing is also needed. --- cmake_targets/CMakeLists.txt | 108 +- executables/nr-softmodem.c | 7 +- openair2/LAYER2/MAC/main.c | 4 +- openair2/LAYER2/MAC/main_ue.c | 2 +- openair2/LAYER2/NR_MAC_UE/main_ue_nr.c | 2 +- openair2/LAYER2/NR_MAC_gNB/main.c | 2 +- openair2/LAYER2/RLC/rlc.c | 3 +- openair2/LAYER2/RLC/rlc.h | 4 +- openair2/LAYER2/nr_rlc/Makefile | 15 + openair2/LAYER2/nr_rlc/TODO | 10 + openair2/LAYER2/nr_rlc/asn1_utils.c | 129 ++ openair2/LAYER2/nr_rlc/asn1_utils.h | 33 + openair2/LAYER2/nr_rlc/nr_rlc_entity.c | 174 ++ openair2/LAYER2/nr_rlc/nr_rlc_entity.h | 104 ++ openair2/LAYER2/nr_rlc/nr_rlc_entity_am.c | 1844 ++++++++++++++++++++ openair2/LAYER2/nr_rlc/nr_rlc_entity_am.h | 102 ++ openair2/LAYER2/nr_rlc/nr_rlc_entity_tm.c | 181 ++ openair2/LAYER2/nr_rlc/nr_rlc_entity_tm.h | 55 + openair2/LAYER2/nr_rlc/nr_rlc_entity_um.c | 697 ++++++++ openair2/LAYER2/nr_rlc/nr_rlc_entity_um.h | 79 + openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c | 874 ++++++++++ openair2/LAYER2/nr_rlc/nr_rlc_pdu.c | 190 ++ openair2/LAYER2/nr_rlc/nr_rlc_pdu.h | 84 + openair2/LAYER2/nr_rlc/nr_rlc_sdu.c | 121 ++ openair2/LAYER2/nr_rlc/nr_rlc_sdu.h | 56 + openair2/LAYER2/nr_rlc/nr_rlc_ue_manager.c | 190 ++ openair2/LAYER2/nr_rlc/nr_rlc_ue_manager.h | 60 + openair2/LAYER2/nr_rlc/test.c | 32 + openair2/LAYER2/nr_rlc/tests/LOG/log.h | 10 + openair2/LAYER2/nr_rlc/tests/Makefile | 33 + openair2/LAYER2/nr_rlc/tests/run_tests.sh | 12 + openair2/LAYER2/nr_rlc/tests/test.c | 483 +++++ openair2/LAYER2/nr_rlc/tests/test1.h | 14 + openair2/LAYER2/nr_rlc/tests/test1.txt.gz | Bin 0 -> 246 bytes openair2/LAYER2/nr_rlc/tests/test10.h | 13 + openair2/LAYER2/nr_rlc/tests/test10.txt.gz | Bin 0 -> 248 bytes openair2/LAYER2/nr_rlc/tests/test11.h | 13 + openair2/LAYER2/nr_rlc/tests/test11.txt.gz | Bin 0 -> 251 bytes openair2/LAYER2/nr_rlc/tests/test12.h | 18 + openair2/LAYER2/nr_rlc/tests/test12.txt.gz | Bin 0 -> 227 bytes openair2/LAYER2/nr_rlc/tests/test13.h | 18 + openair2/LAYER2/nr_rlc/tests/test13.txt.gz | Bin 0 -> 230 bytes openair2/LAYER2/nr_rlc/tests/test2.h | 14 + openair2/LAYER2/nr_rlc/tests/test2.txt.gz | Bin 0 -> 250 bytes openair2/LAYER2/nr_rlc/tests/test3.h | 14 + openair2/LAYER2/nr_rlc/tests/test3.txt.gz | Bin 0 -> 200 bytes openair2/LAYER2/nr_rlc/tests/test4.h | 14 + openair2/LAYER2/nr_rlc/tests/test4.txt.gz | Bin 0 -> 200 bytes openair2/LAYER2/nr_rlc/tests/test5.h | 15 + openair2/LAYER2/nr_rlc/tests/test5.txt.gz | Bin 0 -> 42 bytes openair2/LAYER2/nr_rlc/tests/test6.h | 15 + openair2/LAYER2/nr_rlc/tests/test6.txt.gz | Bin 0 -> 42 bytes openair2/LAYER2/nr_rlc/tests/test7.h | 14 + openair2/LAYER2/nr_rlc/tests/test7.txt.gz | Bin 0 -> 192 bytes openair2/LAYER2/nr_rlc/tests/test8.h | 20 + openair2/LAYER2/nr_rlc/tests/test8.txt.gz | Bin 0 -> 330 bytes openair2/LAYER2/nr_rlc/tests/test9.h | 20 + openair2/LAYER2/nr_rlc/tests/test9.txt.gz | Bin 0 -> 328 bytes targets/TEST/PDCP/with_rlc/test_pdcp_rlc.c | 2 +- 59 files changed, 5824 insertions(+), 80 deletions(-) create mode 100644 openair2/LAYER2/nr_rlc/Makefile create mode 100644 openair2/LAYER2/nr_rlc/TODO create mode 100644 openair2/LAYER2/nr_rlc/asn1_utils.c create mode 100644 openair2/LAYER2/nr_rlc/asn1_utils.h create mode 100644 openair2/LAYER2/nr_rlc/nr_rlc_entity.c create mode 100644 openair2/LAYER2/nr_rlc/nr_rlc_entity.h create mode 100644 openair2/LAYER2/nr_rlc/nr_rlc_entity_am.c create mode 100644 openair2/LAYER2/nr_rlc/nr_rlc_entity_am.h create mode 100644 openair2/LAYER2/nr_rlc/nr_rlc_entity_tm.c create mode 100644 openair2/LAYER2/nr_rlc/nr_rlc_entity_tm.h create mode 100644 openair2/LAYER2/nr_rlc/nr_rlc_entity_um.c create mode 100644 openair2/LAYER2/nr_rlc/nr_rlc_entity_um.h create mode 100644 openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c create mode 100644 openair2/LAYER2/nr_rlc/nr_rlc_pdu.c create mode 100644 openair2/LAYER2/nr_rlc/nr_rlc_pdu.h create mode 100644 openair2/LAYER2/nr_rlc/nr_rlc_sdu.c create mode 100644 openair2/LAYER2/nr_rlc/nr_rlc_sdu.h create mode 100644 openair2/LAYER2/nr_rlc/nr_rlc_ue_manager.c create mode 100644 openair2/LAYER2/nr_rlc/nr_rlc_ue_manager.h create mode 100644 openair2/LAYER2/nr_rlc/test.c create mode 100644 openair2/LAYER2/nr_rlc/tests/LOG/log.h create mode 100644 openair2/LAYER2/nr_rlc/tests/Makefile create mode 100755 openair2/LAYER2/nr_rlc/tests/run_tests.sh create mode 100644 openair2/LAYER2/nr_rlc/tests/test.c create mode 100644 openair2/LAYER2/nr_rlc/tests/test1.h create mode 100644 openair2/LAYER2/nr_rlc/tests/test1.txt.gz create mode 100644 openair2/LAYER2/nr_rlc/tests/test10.h create mode 100644 openair2/LAYER2/nr_rlc/tests/test10.txt.gz create mode 100644 openair2/LAYER2/nr_rlc/tests/test11.h create mode 100644 openair2/LAYER2/nr_rlc/tests/test11.txt.gz create mode 100644 openair2/LAYER2/nr_rlc/tests/test12.h create mode 100644 openair2/LAYER2/nr_rlc/tests/test12.txt.gz create mode 100644 openair2/LAYER2/nr_rlc/tests/test13.h create mode 100644 openair2/LAYER2/nr_rlc/tests/test13.txt.gz create mode 100644 openair2/LAYER2/nr_rlc/tests/test2.h create mode 100644 openair2/LAYER2/nr_rlc/tests/test2.txt.gz create mode 100644 openair2/LAYER2/nr_rlc/tests/test3.h create mode 100644 openair2/LAYER2/nr_rlc/tests/test3.txt.gz create mode 100644 openair2/LAYER2/nr_rlc/tests/test4.h create mode 100644 openair2/LAYER2/nr_rlc/tests/test4.txt.gz create mode 100644 openair2/LAYER2/nr_rlc/tests/test5.h create mode 100644 openair2/LAYER2/nr_rlc/tests/test5.txt.gz create mode 100644 openair2/LAYER2/nr_rlc/tests/test6.h create mode 100644 openair2/LAYER2/nr_rlc/tests/test6.txt.gz create mode 100644 openair2/LAYER2/nr_rlc/tests/test7.h create mode 100644 openair2/LAYER2/nr_rlc/tests/test7.txt.gz create mode 100644 openair2/LAYER2/nr_rlc/tests/test8.h create mode 100644 openair2/LAYER2/nr_rlc/tests/test8.txt.gz create mode 100644 openair2/LAYER2/nr_rlc/tests/test9.h create mode 100644 openair2/LAYER2/nr_rlc/tests/test9.txt.gz diff --git a/cmake_targets/CMakeLists.txt b/cmake_targets/CMakeLists.txt index 273aad0c20..a0620d8345 100644 --- a/cmake_targets/CMakeLists.txt +++ b/cmake_targets/CMakeLists.txt @@ -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) diff --git a/executables/nr-softmodem.c b/executables/nr-softmodem.c index 931e2c1cd4..0a638f7265 100644 --- a/executables/nr-softmodem.c +++ b/executables/nr-softmodem.c @@ -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); diff --git a/openair2/LAYER2/MAC/main.c b/openair2/LAYER2/MAC/main.c index 8fb8d44e0a..4e139dcf92 100644 --- a/openair2/LAYER2/MAC/main.c +++ b/openair2/LAYER2/MAC/main.c @@ -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); } diff --git a/openair2/LAYER2/MAC/main_ue.c b/openair2/LAYER2/MAC/main_ue.c index f482806e1d..e8b0593717 100644 --- a/openair2/LAYER2/MAC/main_ue.c +++ b/openair2/LAYER2/MAC/main_ue.c @@ -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); } diff --git a/openair2/LAYER2/NR_MAC_UE/main_ue_nr.c b/openair2/LAYER2/NR_MAC_UE/main_ue_nr.c index c96acccf9b..0478a6b7d5 100644 --- a/openair2/LAYER2/NR_MAC_UE/main_ue_nr.c +++ b/openair2/LAYER2/NR_MAC_UE/main_ue_nr.c @@ -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(); diff --git a/openair2/LAYER2/NR_MAC_gNB/main.c b/openair2/LAYER2/NR_MAC_gNB/main.c index 88f6c38758..c5dad59d98 100644 --- a/openair2/LAYER2/NR_MAC_gNB/main.c +++ b/openair2/LAYER2/NR_MAC_gNB/main.c @@ -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(); diff --git a/openair2/LAYER2/RLC/rlc.c b/openair2/LAYER2/RLC/rlc.c index 1563a01de3..bf51faef75 100644 --- a/openair2/LAYER2/RLC/rlc.c +++ b/openair2/LAYER2/RLC/rlc.c @@ -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; diff --git a/openair2/LAYER2/RLC/rlc.h b/openair2/LAYER2/RLC/rlc.h index dddffed241..ca3aaf1d90 100644 --- a/openair2/LAYER2/RLC/rlc.h +++ b/openair2/LAYER2/RLC/rlc.h @@ -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); /** @} */ diff --git a/openair2/LAYER2/nr_rlc/Makefile b/openair2/LAYER2/nr_rlc/Makefile new file mode 100644 index 0000000000..071930c4f4 --- /dev/null +++ b/openair2/LAYER2/nr_rlc/Makefile @@ -0,0 +1,15 @@ +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 diff --git a/openair2/LAYER2/nr_rlc/TODO b/openair2/LAYER2/nr_rlc/TODO new file mode 100644 index 0000000000..4e2d3147c9 --- /dev/null +++ b/openair2/LAYER2/nr_rlc/TODO @@ -0,0 +1,10 @@ +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. diff --git a/openair2/LAYER2/nr_rlc/asn1_utils.c b/openair2/LAYER2/nr_rlc/asn1_utils.c new file mode 100644 index 0000000000..46f7d90da5 --- /dev/null +++ b/openair2/LAYER2/nr_rlc/asn1_utils.c @@ -0,0 +1,129 @@ +/* + * 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]; +} diff --git a/openair2/LAYER2/nr_rlc/asn1_utils.h b/openair2/LAYER2/nr_rlc/asn1_utils.h new file mode 100644 index 0000000000..61394c9c69 --- /dev/null +++ b/openair2/LAYER2/nr_rlc/asn1_utils.h @@ -0,0 +1,33 @@ +/* + * 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_ */ diff --git a/openair2/LAYER2/nr_rlc/nr_rlc_entity.c b/openair2/LAYER2/nr_rlc/nr_rlc_entity.c new file mode 100644 index 0000000000..fefe2fc77d --- /dev/null +++ b/openair2/LAYER2/nr_rlc/nr_rlc_entity.c @@ -0,0 +1,174 @@ +/* + * 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; +} diff --git a/openair2/LAYER2/nr_rlc/nr_rlc_entity.h b/openair2/LAYER2/nr_rlc/nr_rlc_entity.h new file mode 100644 index 0000000000..5b12a90cfd --- /dev/null +++ b/openair2/LAYER2/nr_rlc/nr_rlc_entity.h @@ -0,0 +1,104 @@ +/* + * 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_ */ diff --git a/openair2/LAYER2/nr_rlc/nr_rlc_entity_am.c b/openair2/LAYER2/nr_rlc/nr_rlc_entity_am.c new file mode 100644 index 0000000000..8f3d49d8c2 --- /dev/null +++ b/openair2/LAYER2/nr_rlc/nr_rlc_entity_am.c @@ -0,0 +1,1844 @@ +/* + * 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); +} diff --git a/openair2/LAYER2/nr_rlc/nr_rlc_entity_am.h b/openair2/LAYER2/nr_rlc/nr_rlc_entity_am.h new file mode 100644 index 0000000000..7a553a22a9 --- /dev/null +++ b/openair2/LAYER2/nr_rlc/nr_rlc_entity_am.h @@ -0,0 +1,102 @@ +/* + * 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_ */ diff --git a/openair2/LAYER2/nr_rlc/nr_rlc_entity_tm.c b/openair2/LAYER2/nr_rlc/nr_rlc_entity_tm.c new file mode 100644 index 0000000000..86a0697624 --- /dev/null +++ b/openair2/LAYER2/nr_rlc/nr_rlc_entity_tm.c @@ -0,0 +1,181 @@ +/* + * 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); +} diff --git a/openair2/LAYER2/nr_rlc/nr_rlc_entity_tm.h b/openair2/LAYER2/nr_rlc/nr_rlc_entity_tm.h new file mode 100644 index 0000000000..7c8c7e40a0 --- /dev/null +++ b/openair2/LAYER2/nr_rlc/nr_rlc_entity_tm.h @@ -0,0 +1,55 @@ +/* + * 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_ */ diff --git a/openair2/LAYER2/nr_rlc/nr_rlc_entity_um.c b/openair2/LAYER2/nr_rlc/nr_rlc_entity_um.c new file mode 100644 index 0000000000..09f29ebe67 --- /dev/null +++ b/openair2/LAYER2/nr_rlc/nr_rlc_entity_um.c @@ -0,0 +1,697 @@ +/* + * 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); +} diff --git a/openair2/LAYER2/nr_rlc/nr_rlc_entity_um.h b/openair2/LAYER2/nr_rlc/nr_rlc_entity_um.h new file mode 100644 index 0000000000..21063a1ea1 --- /dev/null +++ b/openair2/LAYER2/nr_rlc/nr_rlc_entity_um.h @@ -0,0 +1,79 @@ +/* + * 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_ */ diff --git a/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c b/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c new file mode 100644 index 0000000000..b56e8b11e9 --- /dev/null +++ b/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c @@ -0,0 +1,874 @@ +/* + * 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; +} diff --git a/openair2/LAYER2/nr_rlc/nr_rlc_pdu.c b/openair2/LAYER2/nr_rlc/nr_rlc_pdu.c new file mode 100644 index 0000000000..b42410ab72 --- /dev/null +++ b/openair2/LAYER2/nr_rlc/nr_rlc_pdu.c @@ -0,0 +1,190 @@ +/* + * 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); +} diff --git a/openair2/LAYER2/nr_rlc/nr_rlc_pdu.h b/openair2/LAYER2/nr_rlc/nr_rlc_pdu.h new file mode 100644 index 0000000000..8e9872522f --- /dev/null +++ b/openair2/LAYER2/nr_rlc/nr_rlc_pdu.h @@ -0,0 +1,84 @@ +/* + * 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_ */ diff --git a/openair2/LAYER2/nr_rlc/nr_rlc_sdu.c b/openair2/LAYER2/nr_rlc/nr_rlc_sdu.c new file mode 100644 index 0000000000..ef0d52558a --- /dev/null +++ b/openair2/LAYER2/nr_rlc/nr_rlc_sdu.c @@ -0,0 +1,121 @@ +/* + * 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); + } +} diff --git a/openair2/LAYER2/nr_rlc/nr_rlc_sdu.h b/openair2/LAYER2/nr_rlc/nr_rlc_sdu.h new file mode 100644 index 0000000000..f4585fb7d6 --- /dev/null +++ b/openair2/LAYER2/nr_rlc/nr_rlc_sdu.h @@ -0,0 +1,56 @@ +/* + * 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_ */ diff --git a/openair2/LAYER2/nr_rlc/nr_rlc_ue_manager.c b/openair2/LAYER2/nr_rlc/nr_rlc_ue_manager.c new file mode 100644 index 0000000000..09f1e4f599 --- /dev/null +++ b/openair2/LAYER2/nr_rlc/nr_rlc_ue_manager.c @@ -0,0 +1,190 @@ +/* + * 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; +} diff --git a/openair2/LAYER2/nr_rlc/nr_rlc_ue_manager.h b/openair2/LAYER2/nr_rlc/nr_rlc_ue_manager.h new file mode 100644 index 0000000000..97da28cdce --- /dev/null +++ b/openair2/LAYER2/nr_rlc/nr_rlc_ue_manager.h @@ -0,0 +1,60 @@ +/* + * 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_ */ diff --git a/openair2/LAYER2/nr_rlc/test.c b/openair2/LAYER2/nr_rlc/test.c new file mode 100644 index 0000000000..52b0891bc3 --- /dev/null +++ b/openair2/LAYER2/nr_rlc/test.c @@ -0,0 +1,32 @@ +#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; +} diff --git a/openair2/LAYER2/nr_rlc/tests/LOG/log.h b/openair2/LAYER2/nr_rlc/tests/LOG/log.h new file mode 100644 index 0000000000..5c9fcd643c --- /dev/null +++ b/openair2/LAYER2/nr_rlc/tests/LOG/log.h @@ -0,0 +1,10 @@ +#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_ */ diff --git a/openair2/LAYER2/nr_rlc/tests/Makefile b/openair2/LAYER2/nr_rlc/tests/Makefile new file mode 100644 index 0000000000..746d61483b --- /dev/null +++ b/openair2/LAYER2/nr_rlc/tests/Makefile @@ -0,0 +1,33 @@ +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 diff --git a/openair2/LAYER2/nr_rlc/tests/run_tests.sh b/openair2/LAYER2/nr_rlc/tests/run_tests.sh new file mode 100755 index 0000000000..ac22516064 --- /dev/null +++ b/openair2/LAYER2/nr_rlc/tests/run_tests.sh @@ -0,0 +1,12 @@ +#!/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 diff --git a/openair2/LAYER2/nr_rlc/tests/test.c b/openair2/LAYER2/nr_rlc/tests/test.c new file mode 100644 index 0000000000..742e79ca2c --- /dev/null +++ b/openair2/LAYER2/nr_rlc/tests/test.c @@ -0,0 +1,483 @@ +#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; +} diff --git a/openair2/LAYER2/nr_rlc/tests/test1.h b/openair2/LAYER2/nr_rlc/tests/test1.h new file mode 100644 index 0000000000..3240ecfc36 --- /dev/null +++ b/openair2/LAYER2/nr_rlc/tests/test1.h @@ -0,0 +1,14 @@ +/* + * 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 diff --git a/openair2/LAYER2/nr_rlc/tests/test1.txt.gz b/openair2/LAYER2/nr_rlc/tests/test1.txt.gz new file mode 100644 index 0000000000000000000000000000000000000000..d3852d070cb17dae5f7db1678ef427b0e7f85a96 GIT binary patch literal 246 zcmV<S015veiwFp0zp`Bd19W9`bTKY;cys`jkUek0KoCTGe#MleiN-h%!DR%Zqrp@N zS&nZVS+Y=c9|ZF2F-IJMV@syGEpFe=ykxOXVi=ogJ4*b#T)oE#Vra<^sZkE&Qh`tP zr}9S-Fvb=n7=RNn1Yf`id<CcA8#vpwK%cS~omol0lxrxH3nixq4Pk8-9;Kuz52CT2 zZ<;tm)BF-XTv3Ql>%!I<U%Y-(&kA9W*NJuczxjT@qfB^XgzfT|q{Me-U|gPyF@>wD wQ&U9L^DUlz)}#N@cdf;aVADRy_aWKgHR`>0-Fd)-4>-ug8B~lLdyE1A0C8V&X#fBK literal 0 HcmV?d00001 diff --git a/openair2/LAYER2/nr_rlc/tests/test10.h b/openair2/LAYER2/nr_rlc/tests/test10.h new file mode 100644 index 0000000000..1653736f26 --- /dev/null +++ b/openair2/LAYER2/nr_rlc/tests/test10.h @@ -0,0 +1,13 @@ +/* + * 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 diff --git a/openair2/LAYER2/nr_rlc/tests/test10.txt.gz b/openair2/LAYER2/nr_rlc/tests/test10.txt.gz new file mode 100644 index 0000000000000000000000000000000000000000..12430b53c5647e7c17d1428aa1413ada5b7e5b67 GIT binary patch literal 248 zcmV<U00;jciwFpl0<&EL19W9`bTKe4ba-?C&5^lI!%z@Kd%oh7(A}Bmy+lNGG;u0~ zBFBD4mJmXYlY*}&wx9??2th+P#mt<$)T?z}v7ef8x~P}=_U^jED#q9zxBKqE<<Oz4 z)6(!7NmX%=gbX<fC_yR8P>u>zq6Y0yi%y3T^_Vcj91B>2rC5gLSb>#TgYB>u>#&IR zxNyTA4|sy7c!uYAftPrL@9-Ay@QC*W5$xfUfyek9$7YJ#m+tVc-hEb|=#K`@Nd66} yT}ZRtUphV`g)d1KW>T?`=6rq<(Pp)L>*CXJj3eut%}2Zcx7;@}2S^dB0ssK%)_l4E literal 0 HcmV?d00001 diff --git a/openair2/LAYER2/nr_rlc/tests/test11.h b/openair2/LAYER2/nr_rlc/tests/test11.h new file mode 100644 index 0000000000..41465e09e1 --- /dev/null +++ b/openair2/LAYER2/nr_rlc/tests/test11.h @@ -0,0 +1,13 @@ +/* + * 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 diff --git a/openair2/LAYER2/nr_rlc/tests/test11.txt.gz b/openair2/LAYER2/nr_rlc/tests/test11.txt.gz new file mode 100644 index 0000000000000000000000000000000000000000..1a5294ffa023c3fa3a2b2853e0f846fbbc9d4807 GIT binary patch literal 251 zcmV<X00jRZiwFol1G8NM19W9`bTKh5ba-?C&5^xM!%z%Hcb?*mu-U%(9}!hImZlRz z)lKeA6$zm#O*`=PG!Yq!5JH{UJlH-ypX=48t~ic<81L%2UOzupXvGlQ^X?c<-1h-_ zxHR=2kyI*P5J40%#32ciA{mk+1yUj&X^<AV3`V3w1y$5ghbCx>W@wHUXo-5XL0dGS z5$!O+6f?|W36^3RmSY80VjgR-77JL!I-KD2^_##xzQ@6jad!x(uj|dG>l2v;Zg;pM z*)ODaAx+Y^cwdp+k0i||Qn8Sxp#GlKb~UdJ@vT3{fz8u)dGr7M@+WSJh%B=L0034L BaJ~Ql literal 0 HcmV?d00001 diff --git a/openair2/LAYER2/nr_rlc/tests/test12.h b/openair2/LAYER2/nr_rlc/tests/test12.h new file mode 100644 index 0000000000..4ab28dd3f2 --- /dev/null +++ b/openair2/LAYER2/nr_rlc/tests/test12.h @@ -0,0 +1,18 @@ +/* + * 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 diff --git a/openair2/LAYER2/nr_rlc/tests/test12.txt.gz b/openair2/LAYER2/nr_rlc/tests/test12.txt.gz new file mode 100644 index 0000000000000000000000000000000000000000..ac85aa89f39fb5c76f5e21b07d1658c461839e0c GIT binary patch literal 227 zcmV<90381xiwFqg1hZWN19W9`bTKk6ba-?CrI0;O!$1&3dw#_$p_|?FcZr1PXmE#6 zbUvR^ng}@#1;3uyq6tDNpxa_rZ{G9|&wa;niSu&PZ};QVeTQ|-IeqMo<B8k-i0;mz z{b#0B#|siN<S3v5m8e2BYEX+J8c;&#$BZUSm|>0utiVdF!fLF+S}bA%maq}a*n|r= z-0^@Hc!^hdjW>9UM|{8&KH?dl2qM_)4}rIQ$a7rs?tMIcw|8IL7y75b1<9|F(neZ! d|7d(c3cr#ptfXcmt@~V`=nKslBXIBm004?2Y|#J! literal 0 HcmV?d00001 diff --git a/openair2/LAYER2/nr_rlc/tests/test13.h b/openair2/LAYER2/nr_rlc/tests/test13.h new file mode 100644 index 0000000000..fc973fdcff --- /dev/null +++ b/openair2/LAYER2/nr_rlc/tests/test13.h @@ -0,0 +1,18 @@ +/* + * 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 diff --git a/openair2/LAYER2/nr_rlc/tests/test13.txt.gz b/openair2/LAYER2/nr_rlc/tests/test13.txt.gz new file mode 100644 index 0000000000000000000000000000000000000000..3838dcf15214dda2c8bbd7fba5e60c9016b4a8c2 GIT binary patch literal 230 zcmV<C02%)uiwFqs1hZWN19W9`bTKn7ba-?Cosd0G!$1&3dw#_$q1&DFcZr1PXmE#6 zbUvR^ng}@#1;3s+B1I5FDBTvbdh@1#c<wv)OPrUR{&YV+-FJA$oYTi<KOVT<j@aEX zwEfJK*71TF=9sX66<CQ?SdBGUi$!d}5_Wvd*n}JIxbT1%c!^hdjW>9UM|{8&KH?dl zkReAx0Trl36{=B#S`^WM5*krP6Tt}n`a|F?@A4d%ym=oF-|eNfePJho>m4pg`h%3t gq*eMF?+a4+mE_e*YR;r}sQ+yB1=SY*gZTjf0Cp8^d;kCd literal 0 HcmV?d00001 diff --git a/openair2/LAYER2/nr_rlc/tests/test2.h b/openair2/LAYER2/nr_rlc/tests/test2.h new file mode 100644 index 0000000000..7adc349c1e --- /dev/null +++ b/openair2/LAYER2/nr_rlc/tests/test2.h @@ -0,0 +1,14 @@ +/* + * 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 diff --git a/openair2/LAYER2/nr_rlc/tests/test2.txt.gz b/openair2/LAYER2/nr_rlc/tests/test2.txt.gz new file mode 100644 index 0000000000000000000000000000000000000000..04092cdaa2d778f3a65dfbe26e9879a01c740428 GIT binary patch literal 250 zcmV<W00sXaiwFpBzp`Bd19W9`bTTe<cys`jkWEj*Komvy{))Sljk&c{u}cGS=Z3N} zq$!<O)1)C}J^=mo4g)1pimAyY^A_{Y+<UUwJWFA0q3vB3=C}9P6j2H-<u*6U;l5Vj z$UmyT1rbwxfCMA(5sbkxn1D~<GdKZXmL1TitVI`A@{MvWB|cC}1`vng7PCN8($rhg zm`@jNqM?CKgq5o*(YfzzoeACjod;G7yFX6!{m=8|eovpcmEzC1+y9W1*cArGm8BR{ zxu*GQi->yojpr@#$+h=oXR#;P<PXo6IoYE&x%uUB=N~dc#z8WE0HFwOcaj1C0ODhM AegFUf literal 0 HcmV?d00001 diff --git a/openair2/LAYER2/nr_rlc/tests/test3.h b/openair2/LAYER2/nr_rlc/tests/test3.h new file mode 100644 index 0000000000..db5454f8de --- /dev/null +++ b/openair2/LAYER2/nr_rlc/tests/test3.h @@ -0,0 +1,14 @@ +/* + * 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 diff --git a/openair2/LAYER2/nr_rlc/tests/test3.txt.gz b/openair2/LAYER2/nr_rlc/tests/test3.txt.gz new file mode 100644 index 0000000000000000000000000000000000000000..070981d99126bc7e90585422defb1befa6f315bf GIT binary patch literal 200 zcmV;(05|_1iwFpJzp`Bd19W9`bTck=cys`bkTDCwFc5`zf5jVhyQr<&Q7mpQZ6_(A znyVB<Bu$F=>n*{E)(B2{L+-nKU!G=pg4R^pjB=N^yLAGQK=ZMat+E(53N-0IHFt)f z1bYyH1ZWDHf##qH6oVF^C1_O+KqtPit_;hovYsM5<sVRyI*#IV)3HXjDf}(}p7JCd zY+bXKeOqH&*SdeDV-d{FC(W<?K2N`#MBhrv!C=5a98~Zrj{>cuz_JGbGdsJ+0RR9i C$634p literal 0 HcmV?d00001 diff --git a/openair2/LAYER2/nr_rlc/tests/test4.h b/openair2/LAYER2/nr_rlc/tests/test4.h new file mode 100644 index 0000000000..1c9bcbc0b9 --- /dev/null +++ b/openair2/LAYER2/nr_rlc/tests/test4.h @@ -0,0 +1,14 @@ +/* + * 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 diff --git a/openair2/LAYER2/nr_rlc/tests/test4.txt.gz b/openair2/LAYER2/nr_rlc/tests/test4.txt.gz new file mode 100644 index 0000000000000000000000000000000000000000..f4230c8522fe334bb0a8d294b36d62e70c761f88 GIT binary patch literal 200 zcmV;(05|_1iwFpQzp`Bd19W9`bTlq>cys`bkTDCwFc5`zf5jVhyQr<&Q7mpQZ6_(A znyVB<Bu$F=>n*{E)(B2{L+-nKU!G=pg4R^pjB=N^yLAGQK=ZMat+E(53N-0IHFt)f z1bYyH1ZWDHf##qH6oVF^C1_O+KqtPit_;hovYsM5<sVRyI*#IV)3HXjDf}(}p7JCd zY+bXKeOqH&*SdeDV-d{FC(W<?K2N`#MBhrv!C=5a98~Zrj{>cuz_JGbGdsJ+0RR9o C-C4u{ literal 0 HcmV?d00001 diff --git a/openair2/LAYER2/nr_rlc/tests/test5.h b/openair2/LAYER2/nr_rlc/tests/test5.h new file mode 100644 index 0000000000..ea090adcb5 --- /dev/null +++ b/openair2/LAYER2/nr_rlc/tests/test5.h @@ -0,0 +1,15 @@ +/* + * 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 diff --git a/openair2/LAYER2/nr_rlc/tests/test5.txt.gz b/openair2/LAYER2/nr_rlc/tests/test5.txt.gz new file mode 100644 index 0000000000000000000000000000000000000000..812365c1e36f0b492cb94e3e45c4fe74f6d5888d GIT binary patch literal 42 xcmb2|=HTewyD655xg@o?#8j`OqJ)9Fke6>`kcOV8j@CmahFVVX-8>8o3;-0C3vd7c literal 0 HcmV?d00001 diff --git a/openair2/LAYER2/nr_rlc/tests/test6.h b/openair2/LAYER2/nr_rlc/tests/test6.h new file mode 100644 index 0000000000..86d7724dd2 --- /dev/null +++ b/openair2/LAYER2/nr_rlc/tests/test6.h @@ -0,0 +1,15 @@ +/* + * 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 diff --git a/openair2/LAYER2/nr_rlc/tests/test6.txt.gz b/openair2/LAYER2/nr_rlc/tests/test6.txt.gz new file mode 100644 index 0000000000000000000000000000000000000000..1d8575cef043478db1d98f22b0e1aa37dc578bdb GIT binary patch literal 42 xcmb2|=HL)Muql>_xg@o?#7wWGqJ)9Fke6>`kcOV8j@CmahFVVX-8>8o3;+XB3j6>7 literal 0 HcmV?d00001 diff --git a/openair2/LAYER2/nr_rlc/tests/test7.h b/openair2/LAYER2/nr_rlc/tests/test7.h new file mode 100644 index 0000000000..4a380261f4 --- /dev/null +++ b/openair2/LAYER2/nr_rlc/tests/test7.h @@ -0,0 +1,14 @@ +/* + * 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 diff --git a/openair2/LAYER2/nr_rlc/tests/test7.txt.gz b/openair2/LAYER2/nr_rlc/tests/test7.txt.gz new file mode 100644 index 0000000000000000000000000000000000000000..8689670886558883003d140847b37b2f6c723049 GIT binary patch literal 192 zcmV;x06+g9iwFoW!m?cg19W9`bT=+^cys`jkWCAMKomvy{ffJ+-L<lhSw_;Xjk*dM zta*kI5#uP)uTKmk%mP>MF5WqCZxn=)Lg7;DMp2V@`;CI9VC7Wl!ju^22AtGS`9t7Q z;Q$i!z$rKb=b#TRz$Lf>*GWfomaEv*iN2X~xFrr6iIGcQiAA`LTg5=bvCJ~Dx~}TV uqN!Y8-M9#MZ<CdG{^a><5dXVL(gAx0_(Q#BV^3wjhj{`VCg!TZ0RRATD_gGs literal 0 HcmV?d00001 diff --git a/openair2/LAYER2/nr_rlc/tests/test8.h b/openair2/LAYER2/nr_rlc/tests/test8.h new file mode 100644 index 0000000000..c205f7ab57 --- /dev/null +++ b/openair2/LAYER2/nr_rlc/tests/test8.h @@ -0,0 +1,20 @@ +/* + * 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 diff --git a/openair2/LAYER2/nr_rlc/tests/test8.txt.gz b/openair2/LAYER2/nr_rlc/tests/test8.txt.gz new file mode 100644 index 0000000000000000000000000000000000000000..d69e43acdce5dd621905f0c2d731d189b0807e70 GIT binary patch literal 330 zcmV-Q0k!@giwFqI{<2*H19W9`bT}?_cys{Ol)FyDKoCXye8nlDK{Mk=Y?p|LjwVTk zP~^uNMT!uz;~@BYvLO~2NQ5FNkha)jch0?cW_LE3&NA9{-mFKn-TwOKGD9;OS8f(< z)p540kn`=K<%=Vdj9WwyMGOg$5Q&f&86ydjA~{kZC9=KfkQx<KQ9}bXL?bjt$7q74 zXpR<WiB_mXYfLc33=6Oji?A3QV+odGIaXjLR$&gS34}oJJ{h=o53b4AZt+-kd-Y~s zeJVQxr+XNX><FpoN#67+z6Ye>Taw0}<a&}1>gQR_Cx^-geV_dr$OzLTj;r6w)+;+e zO5dd=Z>(^ivG+gB%+KrJx5tb1q{^+98`m(sn)lD;U%Zw`|7Nrf)D1rTA@W6t4^Mc? cTiUKHUE9{(YW3V_>#B><D~;E~0JQ`F02)D^SO5S3 literal 0 HcmV?d00001 diff --git a/openair2/LAYER2/nr_rlc/tests/test9.h b/openair2/LAYER2/nr_rlc/tests/test9.h new file mode 100644 index 0000000000..c3fb0e31d1 --- /dev/null +++ b/openair2/LAYER2/nr_rlc/tests/test9.h @@ -0,0 +1,20 @@ +/* + * 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 diff --git a/openair2/LAYER2/nr_rlc/tests/test9.txt.gz b/openair2/LAYER2/nr_rlc/tests/test9.txt.gz new file mode 100644 index 0000000000000000000000000000000000000000..6def9c20f8dab3b8faa9284fc8cc24dc4201db2e GIT binary patch literal 328 zcmV-O0k{4iiwFqD0kd5K19W9`bU7|`cys{Olfg>EKomvye#KepM&5f#+9pe}xO3BX zB~p^iL<&VDX)5~lc8Y;SZJ}ZpLKd^g%(?f>oB4D$&uBJzy}6k0*LU|f8Jbc1a=UC? z%jL=;SG%89@18_59uPqkF(g7_Btb^V7?~g`k|PCDBD;+qsZc=`H8etFG(kt`7@eRg znxh3;q7L<Fg$bsZVG$N%2{yvU*aS<l94oLAbC|~}A|cYpF9n|bv#;}wU%t5ZGrd_z zpUS>~{JO?~WJgFvj})q(+<ricek5rcNWMo3#!i*Cm>xPm>MlHdgFJD1q`Lbn$sySR zl1eX|b)Gxh!NgEt7~b>WRR|nN&aYQnUo*R1bZ_ThJe@?(JZA?0{XvVrB?=aW*Sw)= a%hESZ)vniXon-H>FFpXwshaG|1ONc&VU^zi literal 0 HcmV?d00001 diff --git a/targets/TEST/PDCP/with_rlc/test_pdcp_rlc.c b/targets/TEST/PDCP/with_rlc/test_pdcp_rlc.c index 9f28fd7c78..378ee95c3b 100644 --- a/targets/TEST/PDCP/with_rlc/test_pdcp_rlc.c +++ b/targets/TEST/PDCP/with_rlc/test_pdcp_rlc.c @@ -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(); -- 2.26.2