diff --git a/CMakeLists.txt b/CMakeLists.txt
index af48eca7b3..8ac21ab5d5 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -32,7 +32,7 @@ set (OPENAIR_DIR ${CMAKE_SOURCE_DIR})
include("cmake_targets/macros.cmake")
if(NOT DEFINED ENV{CPM_SOURCE_CACHE})
- set(CPM_SOURCE_CACHE ~/.cache/cpm/)
+ set(CPM_SOURCE_CACHE ~/.cache/cpm/ CACHE FILEPATH "Directory to download CPM dependencies")
endif()
include("cmake_targets/CPM.cmake")
@@ -1271,6 +1271,7 @@ set(L2_NR_SRC
${NR_RRC_DIR}/rrc_gNB_radio_bearers.c
${NR_RRC_DIR}/rrc_gNB_cuup.c
${NR_RRC_DIR}/rrc_gNB_du.c
+ ${NR_RRC_DIR}/rrc_gNB_mobility.c
)
set(L2_SRC_UE
@@ -1781,7 +1782,7 @@ add_library(LIB_NAS_SIMUE
${NAS_SRC}UE/nas_parser.c
${NAS_SRC}UE/nas_proc.c
${NAS_SRC}UE/nas_user.c
- ${NAS_SRC}NR_UE/nr_nas_msg_sim.c
+ ${NAS_SRC}NR_UE/nr_nas_msg.c
${libnas_api_OBJS}
${libnas_ue_api_OBJS}
${libnas_emm_msg_OBJS}
@@ -1844,8 +1845,6 @@ add_library(NB_IoT MODULE ${NBIOT_SOURCES} )
add_library(LIB_5GNAS_GNB
${NAS_SRC}/COMMON/nr_common.c
- ${NAS_SRC}/gNB/network_process_nas.c
- ${NAS_SRC}/NR_UE/ue_process_nas.c
${OPENAIR3_DIR}//UICC/usim_interface.c
)
diff --git a/ci-scripts/xml_files/t2_offload_dec_nr_ulsim.xml b/ci-scripts/xml_files/t2_offload_dec_nr_ulsim.xml
index b7125f100b..4e44f7f853 100644
--- a/ci-scripts/xml_files/t2_offload_dec_nr_ulsim.xml
+++ b/ci-scripts/xml_files/t2_offload_dec_nr_ulsim.xml
@@ -33,145 +33,163 @@
Run_T2Test
Run nr_ulsim with CPU: SNR = 30, MCS = 5, 106 PRBs, 1 layer
+ true
nr_ulsim
300
- -n100 -s30 -m5 -r106 -R106 -C10 -P
+ -n100 -s30 -S30.2 -m5 -r106 -R106 -C10 -P
Run_T2Test
Run nr_ulsim with T2 LDPC offload: SNR = 30, MCS = 5, 106 PRBs, 1 layer
+ true
nr_ulsim
100
- -n100 -s30 -m5 -r106 -R106 -o -P --ldpc_offload.dpdk_dev d8:00.0
+ -n100 -s30 -S30.2 -m5 -r106 -R106 -o -P --ldpc_offload.dpdk_dev d8:00.0
Run_T2Test
Run nr_ulsim with CPU: SNR = 30, MCS = 15, 106 PRBs, 1 layer
+ true
nr_ulsim
300
- -n100 -s30 -m15 -r106 -R106 -C10 -P
+ -n100 -s30 -S30.2 -m15 -r106 -R106 -C10 -P
Run_T2Test
Run nr_ulsim with T2 LDPC offload: SNR = 30, MCS = 15, 106 PRBs, 1 layer
+ true
nr_ulsim
150
- -n100 -s30 -m15 -r106 -R106 -o -P --ldpc_offload.dpdk_dev d8:00.0
+ -n100 -s30 -S30.2 -m15 -r106 -R106 -o -P --ldpc_offload.dpdk_dev d8:00.0
Run_T2Test
Run nr_ulsim with CPU: SNR = 30, MCS = 25, 106 PRBs, 1 layer
+ true
nr_ulsim
250
- -n100 -s30 -m25 -r106 -R106 -C10 -P
+ -n100 -s30 -S30.2 -m25 -r106 -R106 -C10 -P
Run_T2Test
Run nr_ulsim with T2 LDPC offload: SNR = 30, MCS = 25, 106 PRBs, 1 layer
+ true
nr_ulsim
250
- -n100 -s30 -m25 -r106 -R106 -o -P --ldpc_offload.dpdk_dev d8:00.0
+ -n100 -s30 -S30.2 -m25 -r106 -R106 -o -P --ldpc_offload.dpdk_dev d8:00.0
Run_T2Test
Run nr_ulsim with CPU: SNR = 30, MCS = 5, 273 PRBs, 1 layer
+ true
nr_ulsim
300
- -n100 -s30 -m5 -r273 -R273 -C10 -P
+ -n100 -s30 -S30.2 -m5 -r273 -R273 -C10 -P
Run_T2Test
Run nr_ulsim with T2 LDPC offload: SNR = 30, MCS = 5, 273 PRBs, 1 layer
+ true
nr_ulsim
150
- -n100 -s30 -m5 -r273 -R273 -o -P --ldpc_offload.dpdk_dev d8:00.0
+ -n100 -s30 -S30.2 -m5 -r273 -R273 -o -P --ldpc_offload.dpdk_dev d8:00.0
Run_T2Test
Run nr_ulsim with CPU: SNR = 30, MCS = 15, 273 PRBs, 1 layer
+ true
nr_ulsim
400
- -n100 -s30 -m15 -r273 -R273 -C10 -P
+ -n100 -s30 -S30.2 -m15 -r273 -R273 -C10 -P
Run_T2Test
Run nr_ulsim with T2 LDPC offload: SNR = 30, MCS = 15, 273 PRBs, 1 layer
+ true
nr_ulsim
350
- -n100 -s30 -m15 -r273 -R273 -o -P --ldpc_offload.dpdk_dev d8:00.0
+ -n100 -s30 -S30.2 -m15 -r273 -R273 -o -P --ldpc_offload.dpdk_dev d8:00.0
Run_T2Test
Run nr_ulsim with CPU: SNR = 30, MCS = 25, 273 PRBs, 1 layer
+ true
nr_ulsim
400
- -n100 -s30 -m25 -r273 -R273 -C10 -P
+ -n100 -s30 -S30.2 -m25 -r273 -R273 -C10 -P
Run_T2Test
Run nr_ulsim with T2 LDPC offload: SNR = 30, MCS = 25, 273 PRBs, 1 layer
+ true
nr_ulsim
550
- -n100 -s30 -m25 -r273 -R273 -o -P --ldpc_offload.dpdk_dev d8:00.0
+ -n100 -s30 -S30.2 -m25 -r273 -R273 -o -P --ldpc_offload.dpdk_dev d8:00.0
Run_T2Test
Run nr_ulsim with CPU: SNR = 30, MCS = 5, 273 PRBs, 2 layers
+ true
nr_ulsim
300
- -n100 -s30 -m5 -r273 -R273 -C10 -W2 -z2 -y2 -P
+ -n100 -s30 -S30.2 -m5 -r273 -R273 -C10 -W2 -z2 -y2 -P
Run_T2Test
Run nr_ulsim with T2 LDPC offload: SNR = 30, MCS = 5, 273 PRBs, 2 layers
+ true
nr_ulsim
250
- -n100 -s30 -m5 -r273 -R273 -o -W2 -z2 -y2 -P --ldpc_offload.dpdk_dev d8:00.0
+ -n100 -s30 -S30.2 -m5 -r273 -R273 -o -W2 -z2 -y2 -P --ldpc_offload.dpdk_dev d8:00.0
Run_T2Test
Run nr_ulsim with CPU: SNR = 30, MCS = 15, 273 PRBs, 2 layers
+ true
nr_ulsim
600
- -n100 -s30 -m15 -r273 -R273 -C10 -W2 -z2 -y2 -P
+ -n100 -s30 -S30.2 -m15 -r273 -R273 -C10 -W2 -z2 -y2 -P
Run_T2Test
Run nr_ulsim with T2 LDPC offload: SNR = 30, MCS = 15, 273 PRBs, 2 layers
+ true
nr_ulsim
650
- -n100 -s30 -m15 -r273 -R273 -o -W2 -z2 -y2 -P --ldpc_offload.dpdk_dev d8:00.0
+ -n100 -s30 -S30.2 -m15 -r273 -R273 -o -W2 -z2 -y2 -P --ldpc_offload.dpdk_dev d8:00.0
Run_T2Test
Run nr_ulsim with CPU: SNR = 30, MCS = 25, 273 PRBs, 2 layers
+ true
nr_ulsim
650
- -n100 -s30 -m25 -r273 -R273 -C10 -W2 -z2 -y2 -P
+ -n100 -s30 -S30.2 -m25 -r273 -R273 -C10 -W2 -z2 -y2 -P
Run_T2Test
Run nr_ulsim with T2 LDPC offload: SNR = 30, MCS = 25, 273 PRBs, 2 layers
+ true
nr_ulsim
1100
- -n100 -s30 -m25 -r273 -R273 -o -W2 -z2 -y2 -P --ldpc_offload.dpdk_dev d8:00.0
+ -n100 -s30 -S30.2 -m25 -r273 -R273 -o -W2 -z2 -y2 -P --ldpc_offload.dpdk_dev d8:00.0
diff --git a/ci-scripts/xml_files/t2_offload_enc_nr_dlsim.xml b/ci-scripts/xml_files/t2_offload_enc_nr_dlsim.xml
index 9a9b7b2437..cff66b56a4 100644
--- a/ci-scripts/xml_files/t2_offload_enc_nr_dlsim.xml
+++ b/ci-scripts/xml_files/t2_offload_enc_nr_dlsim.xml
@@ -27,151 +27,223 @@
000111 000112 000121 000122 000131 000132
000211 000212 000221 000222 000231 000232
000311 000312 000321 000322 000331 000332
+ 000411 000412 000421 000422 000431 000432
Run_T2Test
Run nr_dlsim with CPU: SNR = 30, MCS = 5, 106 PRBs, 1 layer
+ true
nr_dlsim
- 200
- -n100 -s30 -e5 -b106 -R106 -X8 -P
+ 230
+ -n100 -s30 -S30.2 -e5 -b106 -R106 -X 8,9,10,11,12 -P
Run_T2Test
Run nr_dlsim with T2 LDPC offload: SNR = 30, MCS = 5, 106 PRBs, 1 layer
+ true
nr_dlsim
100
- -n100 -s30 -e5 -b106 -R106 -c -P --ldpc_offload.dpdk_dev d8:00.0
+ -n100 -s30 -S30.2 -e5 -b106 -R106 -c -P --ldpc_offload.dpdk_dev d8:00.0
Run_T2Test
Run nr_dlsim with CPU: SNR = 30, MCS = 15, 106 PRBs, 1 layer
+ true
nr_dlsim
- 3000
- -n100 -s30 -e15 -b106 -R106 -X8 -P
+ 300
+ -n100 -s30 -S30.2 -e15 -b106 -R106 -X 8,9,10,11,12 -P
Run_T2Test
Run nr_dlsim with T2 LDPC offload: SNR = 30, MCS = 15, 106 PRBs, 1 layer
+ true
nr_dlsim
100
- -n100 -s30 -e15 -b106 -R106 -c -P --ldpc_offload.dpdk_dev d8:00.0
+ -n100 -s30 -S30.2 -e15 -b106 -R106 -c -P --ldpc_offload.dpdk_dev d8:00.0
Run_T2Test
Run nr_dlsim with CPU: SNR = 30, MCS = 25, 106 PRBs, 1 layer
+ true
nr_dlsim
- 400
- -n100 -s30 -e25 -b106 -R106 -X8 -P
+ 350
+ -n100 -s30 -S30.2 -e25 -b106 -R106 -X 8,9,10,11,12 -P
Run_T2Test
Run nr_dlsim with T2 LDPC offload: SNR = 30, MCS = 25, 106 PRBs, 1 layer
+ true
nr_dlsim
200
- -n100 -s30 -e25 -b106 -R106 -c -P --ldpc_offload.dpdk_dev d8:00.0
+ -n100 -s30 -S30.2 -e25 -b106 -R106 -c -P --ldpc_offload.dpdk_dev d8:00.0
Run_T2Test
Run nr_dlsim with CPU: SNR = 30, MCS = 5, 273 PRBs, 1 layer
+ true
nr_dlsim
300
- -n100 -s30 -e5 -b273 -R273 -X8 -P
+ -n100 -s30 -S30.2 -e5 -b273 -R273 -X 8,9,10,11,12 -P
Run_T2Test
Run nr_dlsim with T2 LDPC offload: SNR = 30, MCS = 5, 273 PRBs, 1 layer
+ true
nr_dlsim
150
- -n100 -s30 -e5 -b273 -R273 -c -P --ldpc_offload.dpdk_dev d8:00.0
+ -n100 -s30 -S30.2 -e5 -b273 -R273 -c -P --ldpc_offload.dpdk_dev d8:00.0
Run_T2Test
Run nr_dlsim with CPU: SNR = 30, MCS = 15, 273 PRBs, 1 layer
+ true
nr_dlsim
- 450
- -n100 -s30 -e15 -b273 -R273 -X8 -P
+ 350
+ -n100 -s30 -S30.2 -e15 -b273 -R273 -X 8,9,10,11,12 -P
Run_T2Test
Run nr_dlsim with T2 LDPC offload: SNR = 30, MCS = 15, 273 PRBs, 1 layer
+ true
nr_dlsim
250
- -n100 -s30 -e15 -b273 -R273 -c -P --ldpc_offload.dpdk_dev d8:00.0
+ -n100 -s30 -S30.2 -e15 -b273 -R273 -c -P --ldpc_offload.dpdk_dev d8:00.0
Run_T2Test
Run nr_dlsim with CPU: SNR = 30, MCS = 25, 273 PRBs, 1 layer
+ true
nr_dlsim
- 500
- -n100 -s30 -e25 -b273 -R273 -X8 -P
+ 400
+ -n100 -s30 -S30.2 -e25 -b273 -R273 -X 8,9,10,11,12 -P
Run_T2Test
Run nr_dlsim with T2 LDPC offload: SNR = 30, MCS = 25, 273 PRBs, 1 layer
+ true
nr_dlsim
400
- -n100 -s30 -e25 -b273 -R273 -c -P --ldpc_offload.dpdk_dev d8:00.0
+ -n100 -s30 -S30.2 -e25 -b273 -R273 -c -P --ldpc_offload.dpdk_dev d8:00.0
Run_T2Test
Run nr_dlsim with CPU: SNR = 30, MCS = 5, 273 PRBs, 2 layers
+ true
nr_dlsim
- 350
- -n100 -s30 -e5 -b273 -R273 -X8 -x2 -z2 -y2 -P
+ 400
+ -n100 -s30 -S30.2 -e5 -b273 -R273 -X 8,9,10,11,12 -x2 -z2 -y2 -P
Run_T2Test
Run nr_dlsim with T2 LDPC offload: SNR = 30, MCS = 5, 273 PRBs, 2 layers
+ true
nr_dlsim
200
- -n100 -s30 -e5 -b273 -R273 -c -x2 -z2 -y2 -P --ldpc_offload.dpdk_dev d8:00.0
+ -n100 -s30 -S30.2 -e5 -b273 -R273 -c -x2 -z2 -y2 -P --ldpc_offload.dpdk_dev d8:00.0
Run_T2Test
Run nr_dlsim with CPU: SNR = 30, MCS = 15, 273 PRBs, 2 layers
+ true
nr_dlsim
- 600
- -n100 -s30 -e15 -b273 -R273 -X8 -x2 -z2 -y2 -P
+ 450
+ -n100 -s30 -S30.2 -e15 -b273 -R273 -X 8,9,10,11,12 -x2 -z2 -y2 -P
Run_T2Test
Run nr_dlsim with T2 LDPC offload: SNR = 30, MCS = 15, 273 PRBs, 2 layers
+ true
nr_dlsim
500
- -n100 -s30 -e15 -b273 -R273 -c -x2 -z2 -y2 -P --ldpc_offload.dpdk_dev d8:00.0
+ -n100 -s30 -S30.2 -e15 -b273 -R273 -c -x2 -z2 -y2 -P --ldpc_offload.dpdk_dev d8:00.0
Run_T2Test
Run nr_dlsim with CPU: SNR = 30, MCS = 25, 273 PRBs, 2 layers
+ true
nr_dlsim
- 800
- -n100 -s30 -e25 -b273 -R273 -X8 -x2 -z2 -y2 -P
+ 500
+ -n100 -s30 -S30.2 -e25 -b273 -R273 -X 8,9,10,11,12 -x2 -z2 -y2 -P
Run_T2Test
Run nr_dlsim with T2 LDPC offload: SNR = 30, MCS = 25, 273 PRBs, 2 layers
+ true
nr_dlsim
- 800
- -n100 -s30 -e25 -b273 -R273 -c -x2 -z2 -y2 -P --ldpc_offload.dpdk_dev d8:00.0
+ 500
+ -n100 -s30 -S30.2 -e25 -b273 -R273 -c -x2 -z2 -y2 -P --ldpc_offload.dpdk_dev d8:00.0
+
+ Run_T2Test
+ Run nr_dlsim with CPU: SNR = 30, MCS = 5, 273 PRBs, 2 layers, 4 antennas
+ true
+ nr_dlsim
+ 400
+ -n100 -s30 -S30.2 -e5 -b273 -R273 -X8,9,10,11,12 -x2 -z4 -y4 -P
+
+
+
+ Run_T2Test
+ Run nr_dlsim with T2 LDPC offload: SNR = 30, MCS = 5, 273 PRBs, 2 layers, 4 antennas
+ true
+ nr_dlsim
+ 150
+ -n100 -s30 -S30.2 -e5 -b273 -R273 -c -x2 -z4 -y4 -P --ldpc_offload.dpdk_dev d8:00.0
+
+
+
+ Run_T2Test
+ Run nr_dlsim with CPU: SNR = 30, MCS = 20, 273 PRBs, 2 layers, 4 antennas
+ true
+ nr_dlsim
+ 400
+ -n100 -s30 -S30.2 -e15 -b273 -R273 -X8,9,10,11,12 -x2 -z4 -y4 -P
+
+
+
+ Run_T2Test
+ Run nr_dlsim with T2 LDPC offload: SNR = 30, MCS = 20, 273 PRBs, 2 layers, 4 antennas
+ true
+ nr_dlsim
+ 300
+ -n100 -s30 -S30.2 -e15 -b273 -R273 -c -x2 -z4 -y4 -P --ldpc_offload.dpdk_dev d8:00.0
+
+
+
+ Run_T2Test
+ Run nr_dlsim with CPU: SNR = 30, MCS = 27, 273 PRBs, 2 layers, 4 antennas
+ true
+ nr_dlsim
+ 450
+ -n100 -s30 -S30.2 -e25 -b273 -R273 -X8,9,10,11,12 -x2 -z4 -y4 -P
+
+
+
+ Run_T2Test
+ Run nr_dlsim with T2 LDPC offload: SNR = 30, MCS = 27, 273 PRBs, 2 layers, 4 antennas
+ true
+ nr_dlsim
+ 450
+ -n100 -s30 -S30.2 -e25 -b273 -R273 -c -x2 -z4 -y4 -P --ldpc_offload.dpdk_dev d8:00.0
+
diff --git a/common/config/yaml/CMakeLists.txt b/common/config/yaml/CMakeLists.txt
index 5ff8c23eb1..635887e37e 100644
--- a/common/config/yaml/CMakeLists.txt
+++ b/common/config/yaml/CMakeLists.txt
@@ -1,25 +1,18 @@
-find_package(yaml-cpp REQUIRED PATHS /usr/lib/aarch64-linux-gnu/pkgconfig/)
-# include(FetchContent)
-
-# FetchContent_Declare(
-# yaml-cpp
-# GIT_REPOSITORY https://github.com/jbeder/yaml-cpp.git
-# GIT_TAG 0.8.0
-# )
-# FetchContent_GetProperties(yaml-cpp)
-
-# if(NOT yaml-cpp_POPULATED)
-# message(STATUS "Fetching yaml-cpp...")
-# FetchContent_Populate(yaml-cpp)
-# add_subdirectory(${yaml-cpp_SOURCE_DIR} ${yaml-cpp_BINARY_DIR})
-# endif()
+find_package(yaml-cpp)
+if (NOT yaml-cpp_FOUND)
+ CPMAddPackage("gh:jbeder/yaml-cpp#yaml-cpp-0.7.0@0.7.0")
+else()
+ if (NOT TARGET yaml-cpp::yaml-cpp)
+ add_library(yaml-cpp::yaml-cpp ALIAS yaml-cpp)
+ endif()
+endif()
add_library(params_yaml_static config_yaml.cpp)
-target_link_libraries(params_yaml_static PUBLIC UTIL yaml-cpp)
+target_link_libraries(params_yaml_static PUBLIC UTIL yaml-cpp::yaml-cpp)
if (ENABLE_TESTS)
add_subdirectory(tests)
endif()
add_library(params_yaml MODULE config_yaml.cpp)
-target_link_libraries(params_yaml PUBLIC UTIL yaml-cpp)
+target_link_libraries(params_yaml PUBLIC UTIL yaml-cpp::yaml-cpp)
set_target_properties(params_yaml PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
diff --git a/common/utils/CMakeLists.txt b/common/utils/CMakeLists.txt
index 025091249b..7cf067dd14 100644
--- a/common/utils/CMakeLists.txt
+++ b/common/utils/CMakeLists.txt
@@ -16,3 +16,6 @@ add_subdirectory(threadPool)
add_library(utils utils.c system.c time_meas.c time_stat.c tun_if.c)
target_include_directories(utils PUBLIC .)
target_link_libraries(utils PRIVATE ${T_LIB})
+if (ENABLE_TESTS)
+ add_subdirectory(hashtable/tests)
+endif()
diff --git a/common/utils/T/T.h b/common/utils/T/T.h
index 07919e843a..746607cc6e 100644
--- a/common/utils/T/T.h
+++ b/common/utils/T/T.h
@@ -177,6 +177,7 @@ extern int *T_active;
#else /* #ifdef __cplusplus */
/* C version of T_HEADER with time */
+#ifdef CHECK_T_TYPE
#define T_HEADER(x) \
do { \
if (!__builtin_types_compatible_p(typeof(x), struct T_header *)) { \
@@ -191,6 +192,16 @@ extern int *T_active;
T_LOCAL_size += sizeof(struct timespec); \
T_PUT_int(1, (int)(uintptr_t)(x)); \
} while (0)
+#else /* CHECK_T_TYPE */
+#define T_HEADER(x) \
+ do { \
+ struct timespec T_HEADER_time; \
+ if (clock_gettime(CLOCK_REALTIME, &T_HEADER_time)) abort(); \
+ memcpy(T_LOCAL_buf, &T_HEADER_time, sizeof(struct timespec)); \
+ T_LOCAL_size += sizeof(struct timespec); \
+ T_PUT_int(1, (int)(uintptr_t)(x)); \
+ } while (0)
+#endif /* CHECK_T_TYPE */
#endif /* #ifdef __cplusplus */
@@ -207,6 +218,7 @@ extern int *T_active;
#else /* #ifdef __cplusplus */
/* C version of T_HEADER without time */
+#ifdef CHECK_T_TYPE
#define T_HEADER(x) \
do { \
if (!__builtin_types_compatible_p(typeof(x), struct T_header *)) { \
@@ -217,6 +229,12 @@ extern int *T_active;
} \
T_PUT_int(1, (int)(uintptr_t)(x)); \
} while (0)
+#else /* CHECK_T_TYPE */
+#define T_HEADER(x) \
+ do { \
+ T_PUT_int(1, (int)(uintptr_t)(x)); \
+ } while (0)
+#endif /* CHECK_T_TYPE */
#endif /* #ifdef __cplusplus */
diff --git a/common/utils/hashtable/hashtable.c b/common/utils/hashtable/hashtable.c
index e9641f8e0c..bbd03692e4 100644
--- a/common/utils/hashtable/hashtable.c
+++ b/common/utils/hashtable/hashtable.c
@@ -305,3 +305,24 @@ hashtable_rc_t hashtable_get(const hash_table_t *const hashtblP, const hash_key_
*dataP = NULL;
return HASH_TABLE_KEY_NOT_EXISTS;
}
+
+hash_table_iterator_s hashtable_get_iterator(const hash_table_t *const hashtbl)
+{
+ return (hash_table_iterator_s){.index = 0, .node = hashtbl->nodes[0], .hashtbl = hashtbl};
+}
+
+bool hashtable_iterator_getnext(hash_table_iterator_s *iterator, void **dataP)
+{
+ // Iterate over table indexes
+ while (iterator->node == NULL) {
+ iterator->index++;
+ if (iterator->index >= iterator->hashtbl->size) {
+ *dataP = NULL;
+ return false;
+ }
+ iterator->node = iterator->hashtbl->nodes[iterator->index];
+ }
+ *dataP = iterator->node->data;
+ iterator->node = iterator->node->next;
+ return true;
+}
diff --git a/common/utils/hashtable/hashtable.h b/common/utils/hashtable/hashtable.h
index 09f8623ce2..c50c23ae7b 100644
--- a/common/utils/hashtable/hashtable.h
+++ b/common/utils/hashtable/hashtable.h
@@ -24,6 +24,7 @@
#include
#include
#include
+#include
typedef size_t hash_size_t;
typedef uint64_t hash_key_t;
@@ -54,6 +55,12 @@ typedef struct hash_table_s {
void (*freefunc)(void *);
} hash_table_t;
+typedef struct hash_table_iterator_s {
+ int index;
+ hash_node_t* node;
+ const hash_table_t* const hashtbl;
+} hash_table_iterator_s;
+
char *hashtable_rc_code2string(hashtable_rc_t rcP);
void hash_free_int_func(void *memoryP);
hash_table_t *hashtable_create (const hash_size_t size, hash_size_t (*hashfunc)(const hash_key_t ), void (*freefunc)(void *));
@@ -63,8 +70,8 @@ hashtable_rc_t hashtable_dump_content (const hash_table_t *const hashtblP, char
hashtable_rc_t hashtable_insert (hash_table_t *const hashtbl, const hash_key_t key, void *data);
hashtable_rc_t hashtable_remove (hash_table_t *const hashtbl, const hash_key_t key);
hashtable_rc_t hashtable_get (const hash_table_t *const hashtbl, const hash_key_t key, void **dataP);
-
-
+hash_table_iterator_s hashtable_get_iterator(const hash_table_t *const hashtbl);
+bool hashtable_iterator_getnext(hash_table_iterator_s* iterator, void **dataP);
#endif
diff --git a/common/utils/hashtable/tests/CMakeLists.txt b/common/utils/hashtable/tests/CMakeLists.txt
new file mode 100644
index 0000000000..baf2461262
--- /dev/null
+++ b/common/utils/hashtable/tests/CMakeLists.txt
@@ -0,0 +1,5 @@
+add_executable(test_hashtable test_hashtable.cpp)
+add_dependencies(tests test_hashtable)
+target_link_libraries(test_hashtable PRIVATE HASHTABLE GTest::gtest)
+add_test(NAME test_hashtable
+ COMMAND ./test_hashtable)
diff --git a/common/utils/hashtable/tests/test_hashtable.cpp b/common/utils/hashtable/tests/test_hashtable.cpp
new file mode 100644
index 0000000000..aa63caa331
--- /dev/null
+++ b/common/utils/hashtable/tests/test_hashtable.cpp
@@ -0,0 +1,106 @@
+/*
+ * 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
+extern "C" {
+#include "hashtable.h"
+}
+
+TEST(hashtable, test_insert_remove) {
+ hash_table_t* table = hashtable_create(250, NULL, NULL);
+ int* a = (int*)calloc(1, sizeof(*a));
+ EXPECT_EQ(hashtable_insert(table, 1, a), HASH_TABLE_OK);
+ EXPECT_EQ(hashtable_remove(table, 1), HASH_TABLE_OK);
+ EXPECT_EQ(hashtable_destroy(&table), HASH_TABLE_OK);
+}
+
+TEST(hashtable, test_insert_iterate_remove) {
+ hash_table_t* table = hashtable_create(250, NULL, NULL);
+ int* a = (int*)calloc(1, sizeof(*a));
+ EXPECT_EQ(hashtable_insert(table, 1, a), HASH_TABLE_OK);
+ auto iterator = hashtable_get_iterator(table);
+ void* ptr;
+ int num_items = 0;
+ while(hashtable_iterator_getnext(&iterator, &ptr)) {
+ num_items++;
+ }
+ EXPECT_EQ(num_items, 1);
+ EXPECT_EQ(hashtable_remove(table, 1), HASH_TABLE_OK);
+ num_items = 0;
+
+ auto iterator2 = hashtable_get_iterator(table);
+ while(hashtable_iterator_getnext(&iterator2, &ptr)) {
+ num_items++;
+ }
+ EXPECT_EQ(num_items, 0);
+ EXPECT_EQ(hashtable_destroy(&table), HASH_TABLE_OK);
+}
+
+TEST(hashtable, test_insert_three_iterate_remove) {
+ hash_table_t* table = hashtable_create(250, NULL, NULL);
+ int* a1 = (int*)calloc(1, sizeof(*a1));
+ int* a2 = (int*)calloc(1, sizeof(*a2));
+ int* a3 = (int*)calloc(1, sizeof(*a3));
+ EXPECT_EQ(hashtable_insert(table, 1, a1), HASH_TABLE_OK);
+ EXPECT_EQ(hashtable_insert(table, 2, a2), HASH_TABLE_OK);
+ EXPECT_EQ(hashtable_insert(table, 3, a3), HASH_TABLE_OK);
+ auto iterator = hashtable_get_iterator(table);
+ void* ptr;
+ int num_items = 0;
+ while(hashtable_iterator_getnext(&iterator, &ptr)) {
+ num_items++;
+ }
+ EXPECT_EQ(num_items, 3);
+ EXPECT_EQ(hashtable_remove(table, 1), HASH_TABLE_OK);
+ num_items = 0;
+
+ auto iterator2 = hashtable_get_iterator(table);
+ while(hashtable_iterator_getnext(&iterator2, &ptr)) {
+ num_items++;
+ }
+ EXPECT_EQ(num_items, 2);
+ EXPECT_EQ(hashtable_destroy(&table), HASH_TABLE_OK);
+}
+
+TEST(hashtable, test_insert_empty_insert_interate) {
+ hash_table_t* table = hashtable_create(250, NULL, NULL);
+ int* a = (int*)calloc(1, sizeof(*a));
+ EXPECT_EQ(hashtable_insert(table, 1, a), HASH_TABLE_OK);
+ EXPECT_EQ(hashtable_remove(table, 1), HASH_TABLE_OK);
+
+ int* a2 = (int*)calloc(1, sizeof(*a2));
+ EXPECT_EQ(hashtable_insert(table, 1, a2), HASH_TABLE_OK);
+ auto iterator3 = hashtable_get_iterator(table);
+ int num_items = 0;
+ void* ptr;
+ while(hashtable_iterator_getnext(&iterator3, &ptr)) {
+ num_items++;
+ }
+ EXPECT_EQ(num_items, 1);
+
+ EXPECT_EQ(hashtable_destroy(&table), HASH_TABLE_OK);
+}
+
+int main(int argc, char** argv)
+{
+ testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/common/utils/ocp_itti/intertask_interface.h b/common/utils/ocp_itti/intertask_interface.h
index f8f33a9c82..2f2ad54174 100644
--- a/common/utils/ocp_itti/intertask_interface.h
+++ b/common/utils/ocp_itti/intertask_interface.h
@@ -321,8 +321,6 @@ typedef struct {
TASK_DEF(TASK_DU_F1, 200) \
TASK_DEF(TASK_CUCP_E1, 200) \
TASK_DEF(TASK_CUUP_E1, 200) \
- TASK_DEF(TASK_RRC_UE_SIM, 200) \
- TASK_DEF(TASK_RRC_GNB_SIM, 200) \
TASK_DEF(TASK_RRC_NSA_UE, 200) \
TASK_DEF(TASK_RRC_NSA_NRUE, 200) \
TASK_DEF(TASK_NAS_NRUE, 200) \
diff --git a/common/utils/telnetsrv/telnetsrv_ci.c b/common/utils/telnetsrv/telnetsrv_ci.c
index 9f3c03c39a..700ff73135 100644
--- a/common/utils/telnetsrv/telnetsrv_ci.c
+++ b/common/utils/telnetsrv/telnetsrv_ci.c
@@ -71,30 +71,41 @@ int get_single_rnti(char *buf, int debug, telnet_printfunc_t prnt)
return 0;
}
+rrc_gNB_ue_context_t *get_single_rrc_ue(void)
+{
+ rrc_gNB_ue_context_t *ue = NULL;
+ rrc_gNB_ue_context_t *l = NULL;
+ int n = 0;
+ RB_FOREACH (l, rrc_nr_ue_tree_s, &RC.nrrrc[0]->rrc_ue_head) {
+ if (ue == NULL)
+ ue = l;
+ n++;
+ }
+ if (!ue) {
+ printf("could not find any UE in RRC\n");
+ }
+ if (n > 1) {
+ printf("more than one UE in RRC present\n");
+ ue = NULL;
+ }
+
+ return ue;
+}
+
int get_reestab_count(char *buf, int debug, telnet_printfunc_t prnt)
{
if (!RC.nrrrc)
ERROR_MSG_RET("no RRC present, cannot list counts\n");
rrc_gNB_ue_context_t *ue = NULL;
- int rnti = -1;
if (!buf) {
- rrc_gNB_ue_context_t *l = NULL;
- int n = 0;
- RB_FOREACH(l, rrc_nr_ue_tree_s, &RC.nrrrc[0]->rrc_ue_head) {
- if (ue == NULL) ue = l;
- n++;
- }
+ ue = get_single_rrc_ue();
if (!ue)
- ERROR_MSG_RET("could not find any UE in RRC\n");
- if (n > 1)
- ERROR_MSG_RET("more than one UE in RRC present\n");
+ ERROR_MSG_RET("no single UE in RRC present\n");
} else {
- rnti = strtol(buf, NULL, 16);
- if (rnti < 1 || rnti >= 0xfffe)
- ERROR_MSG_RET("RNTI needs to be [1,0xfffe]\n");
- ue = rrc_gNB_get_ue_context_by_rnti_any_du(RC.nrrrc[0], rnti);
+ ue_id_t ue_id = strtol(buf, NULL, 10);
+ ue = rrc_gNB_get_ue_context(RC.nrrrc[0], ue_id);
if (!ue)
- ERROR_MSG_RET("could not find UE with RNTI %04x in RRC\n");
+ ERROR_MSG_RET("could not find UE with ue_id %d in RRC\n");
}
prnt("UE RNTI %04x reestab %d reconfig %d\n",
@@ -125,11 +136,43 @@ int trigger_reestab(char *buf, int debug, telnet_printfunc_t prnt)
return 0;
}
+extern void nr_HO_F1_trigger_telnet(gNB_RRC_INST *rrc, uint32_t rrc_ue_id);
+/**
+ * @brief Trigger F1 handover for UE
+ * @param buf: RRC UE ID or NULL for the first UE in list
+ * @param debug: Debug flag
+ * @param prnt: Print function
+ * @return 0 on success, -1 on failure
+ */
+int rrc_gNB_trigger_f1_ho(char *buf, int debug, telnet_printfunc_t prnt)
+{
+ if (!RC.nrrrc)
+ ERROR_MSG_RET("no RRC present, cannot list counts\n");
+ rrc_gNB_ue_context_t *ue = NULL;
+ if (!buf) {
+ ue = get_single_rrc_ue();
+ if (!ue)
+ ERROR_MSG_RET("no single UE in RRC present\n");
+ } else {
+ ue_id_t ue_id = strtol(buf, NULL, 10);
+ ue = rrc_gNB_get_ue_context(RC.nrrrc[0], ue_id);
+ if (!ue)
+ ERROR_MSG_RET("could not find UE with ue_id %d in RRC\n", ue_id);
+ }
+
+ gNB_RRC_UE_t *UE = &ue->ue_context;
+ nr_HO_F1_trigger_telnet(RC.nrrrc[0], UE->rrc_ue_id);
+ prnt("RRC F1 handover triggered for UE %u\n", UE->rrc_ue_id);
+
+ return 0;
+}
+
static telnetshell_cmddef_t cicmds[] = {
- {"get_single_rnti", "", get_single_rnti},
- {"force_reestab", "[rnti(hex,opt)]", trigger_reestab},
- {"get_reestab_count", "[rnti(hex,opt)]", get_reestab_count},
- {"", "", NULL},
+ {"get_single_rnti", "", get_single_rnti},
+ {"force_reestab", "[rnti(hex,opt)]", trigger_reestab},
+ {"get_reestab_count", "[rnti(hex,opt)]", get_reestab_count},
+ {"trigger_f1_ho", "[rrc_ue_id(int,opt)]", rrc_gNB_trigger_f1_ho},
+ {"", "", NULL},
};
static telnetshell_vardef_t civars[] = {
diff --git a/common/utils/websrv/DOC/websrvuse.md b/common/utils/websrv/DOC/websrvuse.md
index 7f661da2a8..23a59065f0 100644
--- a/common/utils/websrv/DOC/websrvuse.md
+++ b/common/utils/websrv/DOC/websrvuse.md
@@ -268,7 +268,7 @@ The web server back-end is using the [oai configuration module](Config/Rtusage).
| `listenaddr` | `ipV4 address, ascii format` | "0.0.0.0" | local address the back-end is listening on |
| `listenport` | `integer` | 8090 | port number the server is listening on |
| debug | `integer` | 0 | When not 0, http requests headers and json objects dump are added to back-end traces |
-| fpath | character string | websrv | The path to on-disk http server resources . The default value matches the front-end installation when running the softmodem from the executables repository. |
+| fpath | character string | common/utils/websrv | The path to on-disk http server resources . The default value matches the front-end installation when running the softmodem from the executables repository. |
| cert, key, rootca | `character string` | null | certificates and key used to trigger https protocol (not tested) |
| | | | |
diff --git a/common/utils/websrv/websrv.c b/common/utils/websrv/websrv.c
index 33ae8313bf..f6fb898104 100644
--- a/common/utils/websrv/websrv.c
+++ b/common/utils/websrv/websrv.c
@@ -56,7 +56,7 @@ paramdef_t websrvoptions[] = {
{"listenport", "\n", 0, uptr : &(websrvparams.listenport), defuintval : 8090, TYPE_UINT, 0},
{"priority", "\n", 0, uptr : &websrvparams.dbglvl, defuintval : 0, TYPE_UINT, 0},
- {"fpath", "\n", 0, strptr : &websrvparams.fpath, defstrval : "websrv", TYPE_STRING, 0},
+ {"fpath", "\n", 0, strptr : &websrvparams.fpath, defstrval : "common/utils/websrv", TYPE_STRING, 0},
{"cert", "\n", 0, strptr : &websrvparams.certfile, defstrval : NULL, TYPE_STRING, 0},
{"key", "\n", 0, strptr : &websrvparams.keyfile, defstrval : NULL, TYPE_STRING, 0},
{"rootca", "\n", 0, strptr : &websrvparams.rootcafile, defstrval : NULL, TYPE_STRING, 0},
@@ -267,7 +267,7 @@ int websrv_callback_get_softmodemhelp(const struct _u_request *request, struct _
int httpstatus = 204; // no content
char *hlpfile = strstr(request->http_url, "helpfiles");
if (hlpfile != NULL) {
- char *hlppath = malloc(strlen(hlpfile) + strlen(websrvparams.fpath) + 1);
+ char *hlppath = malloc(strlen(hlpfile) + strlen(websrvparams.fpath) + 2);
sprintf(hlppath, "%s/%s", websrvparams.fpath, hlpfile);
help_string = websrv_read_file(hlppath);
if (help_string == NULL) {
@@ -291,9 +291,10 @@ int websrv_callback_default(const struct _u_request *request, struct _u_response
LOG_I(UTIL, "[websrv] Requested file is: %s %s\n", request->http_verb, request->http_url);
websrv_dump_request("default ", request, websrvparams.dbglvl);
- char *fpath = malloc(strlen(request->http_url) + strlen(websrvparams.fpath) + 2);
+ const char *index_file_name = "index.html";
+ char *fpath = malloc(strlen(request->http_url) + strlen(websrvparams.fpath) + strlen(index_file_name) + 2);
if ((strcmp(request->http_url + 1, websrvparams.fpath) == 0) || (strcmp(request->http_url, "/") == 0)) {
- sprintf(fpath, "%s/index.html", websrvparams.fpath);
+ sprintf(fpath, "%s/%s", websrvparams.fpath, index_file_name);
} else {
sprintf(fpath, "%s/%s", websrvparams.fpath, request->http_url);
}
diff --git a/doc/Doxyfile b/doc/Doxyfile
index 360bdd0535..bd1661fe60 100644
--- a/doc/Doxyfile
+++ b/doc/Doxyfile
@@ -1133,9 +1133,8 @@ INPUT = \
@CMAKE_CURRENT_SOURCE_DIR@/../openair3/UICC/usim_interface.h \
@CMAKE_CURRENT_SOURCE_DIR@/../openair3/UICC/usim_interface.c \
@CMAKE_CURRENT_SOURCE_DIR@/../openair3/ocp-gtpu/gtp_itf.h \
-@CMAKE_CURRENT_SOURCE_DIR@/../openair3/NAS/NR_UE/nr_nas_msg_sim.h \
-@CMAKE_CURRENT_SOURCE_DIR@/../openair3/NAS/NR_UE/ue_process_nas.c \
-@CMAKE_CURRENT_SOURCE_DIR@/../openair3/NAS/NR_UE/nr_nas_msg_sim.c \
+@CMAKE_CURRENT_SOURCE_DIR@/../openair3/NAS/NR_UE/nr_nas_msg.h \
+@CMAKE_CURRENT_SOURCE_DIR@/../openair3/NAS/NR_UE/nr_nas_msgc \
@CMAKE_CURRENT_SOURCE_DIR@/../openair3/NAS/UE/nas_itti_messaging.c \
@CMAKE_CURRENT_SOURCE_DIR@/../openair3/NAS/UE/user_defs.h \
@CMAKE_CURRENT_SOURCE_DIR@/../openair3/NAS/UE/EMM/emm_proc.h \
@@ -1584,7 +1583,6 @@ INPUT = \
@CMAKE_CURRENT_SOURCE_DIR@/../openair3/NAS/COMMON/ESM/MSG/ActivateDefaultEpsBearerContextAccept.h \
@CMAKE_CURRENT_SOURCE_DIR@/../openair3/NAS/COMMON/userDef.h \
@CMAKE_CURRENT_SOURCE_DIR@/../openair3/NAS/COMMON/nr_common.c \
-@CMAKE_CURRENT_SOURCE_DIR@/../openair3/NAS/gNB/network_process_nas.c \
@CMAKE_CURRENT_SOURCE_DIR@/../openair3/UTILS/conversions.h \
@CMAKE_CURRENT_SOURCE_DIR@/../openair2/X2AP/x2ap_eNB_encoder.h \
@CMAKE_CURRENT_SOURCE_DIR@/../openair2/X2AP/x2ap_eNB_management_procedures.c \
diff --git a/doc/F1AP/F1-design.md b/doc/F1AP/F1-design.md
index 4690639d5b..06f8a3115b 100644
--- a/doc/F1AP/F1-design.md
+++ b/doc/F1AP/F1-design.md
@@ -59,8 +59,8 @@ This is the current status:
* SA
* Single cell per DU
* Multiple DUs connected to one CU (both CP and UP)
+ * Mobility between DUs connected to a single CU
- Not supported:
- * Mobility
* NSA
## F1-C
diff --git a/doc/FEATURE_SET.md b/doc/FEATURE_SET.md
index eb1027bb3e..21e2f5ca8d 100644
--- a/doc/FEATURE_SET.md
+++ b/doc/FEATURE_SET.md
@@ -137,6 +137,7 @@ These modes of operation are supported:
- evaluation of CQI report
- MAC scheduling of SR reception
- Support of up to 16 UEs (can be increased to 32)
+- Intra-frequency handover
## gNB RLC
@@ -221,6 +222,7 @@ These modes of operation are supported:
- Interface with RRC
- Interface with GTP-u (tunnel creation/handling for F1-U interface)
- One CU(-CP) can handle multiple DUs
+- Support for intra-CU mobility (across DUs)
## gNB E1AP
diff --git a/doc/MAC/mac-usage.md b/doc/MAC/mac-usage.md
index e6241202c4..4c7bf4df8e 100644
--- a/doc/MAC/mac-usage.md
+++ b/doc/MAC/mac-usage.md
@@ -143,10 +143,13 @@ In the last lines:
# Configuration of the MAC
-## Split-related options
+## Split-related options (running in a DU)
See [nFAPI documentation](../L2NFAPI.md) or [Aerial
-tutorial](../Aerial_FAPI_Split_Tutorial.md).
+tutorial](../Aerial_FAPI_Split_Tutorial.md) for information about the FAPI
+split.
+
+See [F1 documentation](../F1-design.md) for information about the F1 split.
## MAC scheduler-related configuration options
diff --git a/doc/README.md b/doc/README.md
index ba7e5fbd12..7fc28f061a 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -53,6 +53,7 @@ There is some general information in the [OpenAirInterface Gitlab Wiki](https://
- [How to run with E2 agent](../openair2/E2AP/README.md)
- [How to run the physical simulators](./physical-simulators.md)
- [How to setup OAI with Nvidia Aerial and Foxconn](./Aerial_FAPI_Split_Tutorial.md)
+- [How to do a handover](./handover-tutorial.md)
Legacy unmaintained files:
- [`L2NFAPI_NOS1.md`](./L2NFAPI_NOS1.md), [`L2NFAPI_S1.md`](./L2NFAPI_S1.md):
diff --git a/doc/RRC/ho.mmd b/doc/RRC/ho.mmd
new file mode 100644
index 0000000000..f86e36a087
--- /dev/null
+++ b/doc/RRC/ho.mmd
@@ -0,0 +1,9 @@
+block-beta
+ columns 5
+ space space DU0 space UE
+ CU space space space space
+ space space DU1 space pos["new pos"]
+ CU<-- "F1" -->DU0
+ CU<-- "F1" -->DU1
+ DU0-- "Uu" -->UE
+ UE-- "move" -->pos
diff --git a/doc/RRC/ho.png b/doc/RRC/ho.png
new file mode 100644
index 0000000000..dd93d6fd1a
Binary files /dev/null and b/doc/RRC/ho.png differ
diff --git a/doc/RRC/rrc-dev.md b/doc/RRC/rrc-dev.md
new file mode 100644
index 0000000000..d511421b01
--- /dev/null
+++ b/doc/RRC/rrc-dev.md
@@ -0,0 +1,318 @@
+This document contains documentation for the 5G RRC layer, destined towards
+developers. It explains the basic working of the RRC, and various UE procedure
+schemes (connection, reestablishment, handover) including their interworking
+with other layers.
+
+User documentation, such as general configuration options, are described in [a
+separate page](./rrc-usage.md).
+
+[[_TOC_]]
+
+# General
+
+5G RRC is basically an ITTI message queue with associated handlers. It
+sequentially reads received ITTI messages and handles them through the function
+`rrc_gnb_task()` (constituing the thread entry point function). In this
+function, one can see three main groups of messages in a big switch statement:
+
+- NGAP (messages starting with `NGAP_`)
+- F1AP (messages starting with `F1AP_`)
+- E1AP (messages starting with `E1AP_`)
+
+For each message, a corresponding handler is called. The messages are roughly
+named according to the 3GPP specification messages, so it should already be
+possible to find the message in the switch based on a message name in the spec.
+
+Note that RRC is inherently single-threaded, and processes messages in a FIFO
+order.
+
+# Sequence Diagrams of UE procedures
+
+The following section presents a number of common UE procedures for connection
+establishment&control, bearer establishment, etc. The intention is to help
+developers find specific functions within RRC in the context of these
+procedures. For more information on message handlers at F1 and E1 layers,
+please refer to the respective [F1 documentation](../F1AP/F1-design.md) and [E1
+documentation](../E1AP/E1-design.md).
+
+For more information on these procedures, please also refer to 3GPP TS 38.401
+(NG-RAN Architecture Description) and O-RAN.WG5.C.1-v11 (NR C-plane profile).
+
+A lot of the following diagrams would show an exchange between CU-CP, DU, and
+UE, in which the CU-CP forwards an "RRC Message" to the UE via an F1AP DL RRC
+Message Transfer through the DU, and correspondingly receives the "RRC Message
+Answer", as follows:
+
+```mermaid
+sequenceDiagram
+ participant ue as UE
+ participant du as DU
+ participant cucp as CU-CP
+ cucp->>du: F1AP DL RRC Msg Transfer (RRC Message)
+ du->>ue: RRC Message
+ ue->>du: RRC Message Answer
+ du->>cucp: F1AP UL RRC Msg Transfer (RRC Message Answer)
+```
+
+To better utilize horizontal space, these exchanges have been collapsed as
+follows, but should be read as the above exchange:
+
+```mermaid
+sequenceDiagram
+ participant ue as UE
+ participant du as DU
+ participant cucp as CU-CP
+ cucp->>ue: F1AP DL RRC Msg Transfer (RRC Message)
+ ue->>cucp: F1AP UL RRC Msg Transfer (RRC Message Answer)
+```
+
+## Initial connection setup/Registration
+
+This sequence diagram shows the principal steps for an initial UE connection.
+This can either happen through a _Registration Request_ (e.g., UE connects
+"from flight mode"), or a _Service Request_ (i.e., UE connects after leaving
+coverage). Both requests are handled similarly by the gNB, and basically
+distinguish themselves, from the point of view of the gNB, by having PDU
+sessions in the NGAP Initial Context Setup Request present (Service Request) or
+not (Registration Request, with following PDU Session Resource Setup Request
+procedure).
+
+```mermaid
+sequenceDiagram
+ participant ue as UE
+ participant du as DU
+ participant cucp as CU-CP
+ participant cuup as CU-UP
+ participant amf as AMF
+
+ ue->>du: Msg1/Preamble
+ du->>ue: Msg2/RAR
+ ue->>cucp: F1AP Initial UL RRC Msg Tr (RRC Setup Req, in Msg3)
+ Note over cucp: rrc_handle_RRCSetupRequest()
+ cucp->>ue: F1AP DL RRC Msg Transfer (RRC Setup, in Msg4)
+ ue->>cucp: F1AP UL RRC Msg Transfer (RRC Setup Complete)
+ Note over cucp: rrc_handle_RRCSetupComplete()
+%% TODO: when is RRC connected?
+ cucp->>amf: NGAP Initial UE Message (NAS Registration/Service Req)
+ Note over amf,ue: NAS Authentication Procedure (see 24.501)
+ Note over amf,ue: NAS Security Procedure (see 24.501)
+ amf->>cucp: NGAP Initial Context Setup Req
+ Note over cucp: rrc_gNB_process_NGAP_INITIAL_CONTEXT_SETUP_REQ()
+ Note over cucp: rrc_gNB_generate_SecurityModeCommand()
+ cucp->>ue: F1AP DL RRC Msg Transfer (RRC Security Mode Command)
+ ue->>cucp: F1AP UL RRC Msg Transfer (RRC Security Mode Complete)
+ Note over cucp: rrc_gNB_decode_dcch() (inline)
+ opt No UE Capabilities present
+ Note over cucp: rrc_gNB_generate_UECapabilityEnquiry()
+ cucp->>ue: F1AP DL RRC Msg Transfer (RRC UE Capability Enquiry)
+ ue->>cucp: F1AP UL RRC Msg Transfer (RRC UE Capability Information)
+ Note over cucp: handle_ueCapabilityInformation()
+ end
+ opt PDU Sessions in NGAP Initial Context Setup Req present
+ Note over cucp: trigger_bearer_setup()
+ cucp->>cuup: E1AP Bearer Context Setup Req
+ cuup->>cucp: E1AP Bearer Context Setup Resp
+ Note over cucp: rrc_gNB_process_e1_bearer_context_setup_resp()
+ cucp->>du: F1AP UE Context Setup Req
+ du->>cucp: F1AP UE Context Setup Resp
+ Note over cucp: rrc_CU_process_ue_context_setup_response()
+ Note over cucp: e1_send_bearer_updates()
+ cucp->>cuup: E1AP Bearer Context Modification Req
+ cucp->>ue: F1AP DL RRC Msg Transfer (RRC Reconfiguration)
+ cuup->>cucp: E1AP Bearer Context Modification Resp
+ ue->>cucp: F1AP UL RRC Msg Transfer (RRC Reconfiguration Complete)
+ Note over cucp: handle_rrcReconfigurationComplete()
+ end
+ Note over cucp: rrc_gNB_send_NGAP_INITIAL_CONTEXT_SETUP_RESP()
+ cucp->>amf: NGAP Initial Context Setup Resp (NAS Registration/Service Complete)
+```
+
+Note that if no PDU session is present in the NGAP Initial UE Context Setup
+Req, no F1AP UE Context Setup will be observed during this initial phase.
+
+If there is no PDU session set up during NGAP Initial Context Setup Req, the UE
+typically requests a PDU session(s) through a NAS procedure, which is basically
+the same code paths as the above optional PDU Session setup during an NGAP
+Initial Context Setup procedure:
+
+```mermaid
+sequenceDiagram
+ participant ue as UE
+ participant du as DU
+ participant cucp as CU-CP
+ participant cuup as CU-UP
+ participant amf as AMF
+
+ ue->>cucp: F1AP UL RRC Msg Transfer (RRC UL Information Transfer)
+ cucp->>amf: NGAP UL NAS Transport (NAS PDU Session Establishment Req)
+ amf->>cucp: NGAP PDU Session Resource Setup Req (NAS PDU Session Establishment Accept)
+ Note over cucp: rrc_gNB_process_NGAP_PDUSESSION_SETUP_REQ()
+ Note over cucp: trigger_bearer_setup()
+ cucp->>cuup: E1AP Bearer Context Setup Req
+ cuup->>cucp: E1AP Bearer Context Setup Resp
+ Note over cucp: rrc_gNB_process_e1_bearer_context_setup_resp()
+ cucp->>du: F1AP UE Context Setup Req
+ du->>cucp: F1AP UE Context Setup Resp
+ Note over cucp: rrc_CU_process_ue_context_setup_response()
+ Note over cucp: e1_send_bearer_updates()
+ cucp->>cuup: E1AP Bearer Context Modification Req
+ cucp->>ue: F1AP DL RRC Msg Transfer (RRC Reconfiguration + NAS PDU Session Establishment Accept)
+ cuup->>cucp: E1AP Bearer Context Modification Resp
+ ue->>cucp: F1AP UL RRC Msg Transfer (RRC Reconfiguration Complete)
+ Note over cucp: handle_rrcReconfigurationComplete()
+ Note over cucp: rrc_gNB_send_NGAP_PDUSESSION_SETUP_RESP()
+ cucp->>amf: NGAP PDU Session Resource Setup Resp
+```
+
+## Reestablishment
+
+The following sequence diagram shows the principal steps during a
+reestablishment request. When handling the RRC Reestablishment Request at the
+CU-CP, it notably checks if this UE is already known, if the UE is still at the
+same DU as it used to be previously (might change because of, or despite
+handover), and other validation checks. If all checks pass, the CU-CP continues
+with the reestablishment procedure; if any check fails, the CU-CP tries to
+release the old UE at the AMF, and continues with the "normal" connection setup
+(registration/service request) described above.
+
+```mermaid
+sequenceDiagram
+ participant ue as UE
+ participant du as DU
+ participant cucp as CU-CP
+ participant cuup as CU-UP
+ participant amf as AMF
+
+ ue->>du: Msg1/Preamble
+ du->>ue: Msg2/RAR
+ ue->>du: Msg3 w/ RRC Reestabishment Request (old C-RNTI/PCI)
+ Note over du: DU creates UE context for new RNTI
+ du->>cucp: F1AP Initial UL RRC Msg Tr (RRC Reestab Req, in Msg3)
+ Note over cucp: rrc_handle_RRCReestablishmentRequest()
+ alt UE known and at the original DU
+ Note over cucp: rrc_gNB_generate_RRCReestablishment()
SRB1 PDCP reestablishment
+ cucp->>du: F1AP DL RRC Msg Tr (old gNB-DU-UE ID)
+ Note over du: Reuse configuration of and delete old UE
(in dl_rrc_message_transfer())
+ du->>ue: RRC Reestablishment
+ ue->>du: RRC Reestablishment Complete
+ du->>cucp: F1AP UL RRC Msg Tr (RRC Reestab Complete)
+ Note over cucp: handle_rrcReestablishmentComplete()
+ Note over cucp: cuup_notify_reestablishment()
+ cucp->>cuup: E1AP Bearer Context Modification Req
+ cucp->>ue: F1AP DL RRC Msg Transfer (RRC Reconfiguration)
+ cuup->>cucp: E1AP Bearer Context Modification Resp
+ ue->>cucp: F1AP UL RRC Msg Transfer (RRC Reconfiguration Complete)
+ Note over cucp: handle_rrcReestablishmentComplete()
+ else Fallback*
+ cucp->>amf: NGAP UE Context Release Request
+ Note over cucp: rrc_gNB_generate_RRCSetup()
+ Note over ue,amf: Continue with connection setup (registration/service request)
+ end
+```
+
+## Handover
+
+The basic handover (HO) structure is as follows. In order to support various
+handover "message passing implementation" (F1AP, NGAP, XnAP), RRC employs
+callbacks to signal HO Accept (`ho_req_ack()`), HO Success (`ho_success()`),
+and HO Cancel (`ho_cancel()`). These can be used to trigger the corresponding
+functionality based on mentioned "message passing implementation".
+
+The following sequence diagram shows the basic functional execution of a
+successful handover in the case of an F1 handover. Note the callbacks as
+mentioned above:
+
+```mermaid
+sequenceDiagram
+ participant ue as UE
+ participant sdu as source DU
+ participant tdu as target DU
+ participant cucp as CU-CP
+ participant cuup as CU-UP
+
+ Note over ue,sdu: UE active on source DU
+ alt HO triggered through A3 event
+ ue->>sdu: RRC Measurement Report
+ sdu->>cucp: F1AP UL RRC Msg Transfer (RRC Measurement Report)
+ Note over cucp: Handover decision (A3 event trigger)
+ else Manual Trigger
+ Note over cucp: Handover decision (e.g., telnet)
+ end
+ Note over cucp: nr_rrc_trigger_f1_ho() ("on source CU")
+ Note over cucp: nr_initiate_handover() ("on target CU")
+ cucp->>tdu: F1AP UE Context Setup Req
+ Note over tdu: Create UE context
+ tdu->>cucp: F1AP UE Context Setup Resp (incl. CellGroupConfig)
+ Note over cucp: rrc_CU_process_ue_context_setup_response() ("on target CU")
+ Note over cucp: cuup_notify_reestablishment()
+ cucp->>cuup: E1AP Bearer Context Modification Req
+ cucp-->>cucp: callback: ho_req_ack()
+ Note over cucp: nr_rrc_f1_ho_acknowledge() ("on source CU")
+ cucp->>sdu: F1AP Context Modification Req (RRC Reconfiguration)
+ cuup->>cucp: E1AP Bearer Context Modification Resp
+ sdu->>ue: RRC Reconfiguration
+ sdu->>cucp: F1AP Context Modification Resp
+ Note over sdu: Stop scheduling UE
+ Note over ue: Resync
+ Note over ue,tdu: RA (Msg1 + Msg2)
+ ue->>tdu: RRC Reconfiguration Complete
+ tdu->>cucp: F1AP UL RRC Msg Transfer (RRC Reconfiguration Complete)
+ Note over cucp: handle_rrcReconfigurationComplete() ("on target CU")
+ cucp-->>cucp: callback: ho_success()
+ Note over cucp: nr_rrc_f1_ho_complete() ("on source CU")
+ cucp->>sdu: F1AP UE Context Release Command
+ sdu->>cucp: F1AP UE Context Release Complete
+ Note over ue,tdu: UE active on target DU
+```
+
+# Structures
+
+## Cells
+
+OAI 5G RRC does not actually handle multiple cells as of now, but multiple DUs,
+each being limited to one cell.
+
+Cell-related data is stored in `nr_rrc_du_container_t`, and kept in a tree
+indexed by the SCTP association ID.
+
+## CU-UPs
+
+CU-UP information is stored in `nr_rrc_cuup_container_t`, and kept in a tree
+indexed by the SCTP association ID.
+
+## Transactions
+
+The RRC keeps track of ongoing transaction (RRC procedures) through a per-UE
+array `xids`, which is indexed with a transaction ID `xid` in `[0,3]` to keep
+track of ongoing transactions. Upon RRC Reconfiguration Complete, these IDs are
+deleted.
+
+Note that while 38.331 requires only one RRC transaction to happen at a time,
+the 5G RRC does not actually ensure this; it only tries to prevent it by
+chaining of transactions and some checks. For instance, the Security Mode
+procedure is followed by a UE capability procedure, and some procedures are not
+executed if others are ongoing (ex.: handover if there is reconfiguratio).
+However, it might be possible to trigger a procedure while another is ongoing.
+
+As of now, no queueing mechanims exists to ensure only one operation is
+ongoing, which would likely also simplify the code.
+
+## Handover
+
+Handover-related data is stored in a per-UE structure of type
+`nr_handover_context_t`. It is a pointer and only set during handover
+operation. This data structure has in turn two pointers, one to the source CU
+(`nr_ho_source_cu_t`), and one to the target CU (`nr_ho_target_cu_t`). In F1,
+both are present. In N2 and Xn, only one pointer is supposed to be set a the
+corresponding CU.
+
+`nr_ho_source_cu_t` contains notably a function pointer `ho_cancel` for
+handover cancel. `nr_ho_target_cu_t` contains function pointers `ho_req_ack`
+for handover request acknowledge and `ho_success` for handover success. As can
+be seen in the sequence diagram above, either the "target CU" or "source CU"
+needs to do an operation, and a "switch" from target to source CU is done using
+these function pointers. For instance, in F1, the handover request acknowledge
+function pointers merely calls another (RRC) function which triggers a
+reconfiguration. In the case of N2, the handover request acknowledge function
+pointer should trigger the NGAP Handover Request Acknowledge, and the handover
+success function pointer should trigger the NGAP Handover Success message.
diff --git a/doc/RRC/rrc-usage.md b/doc/RRC/rrc-usage.md
new file mode 100644
index 0000000000..668c106a0d
--- /dev/null
+++ b/doc/RRC/rrc-usage.md
@@ -0,0 +1,99 @@
+This document describes the basic functioning of the 5G RRC layer, describes
+the periodic output, and explains the various configuration options that
+influence its behavior.
+
+Developer documentation, such as UE connection control flow, reestablishment, or handover, are described in [a separate page](./rrc-dev.md).
+
+[[_TOC_]]
+
+# General
+
+The RRC layer controls the basic connection setup of UEs as well as additional
+procedures. It is the fundamental building block of OAI's CU-CP, and interacts
+with lower layers (DU, basically MAC and RLC) through F1AP messages, and with
+the CU-UP through E1AP messages. More information can be found in the
+respective [F1AP page](../F1-design.md) and [E1AP page](../E1AP/E1-design.md).
+
+# Periodic output and interpretation
+
+Similarly to the scheduler, the RRC periodically prints information about
+connected UEs and DUs into file `nrRRC_stats.log` in the current working
+directory of the executable running the RRC (typically, `nr-softmodem`). The
+output lists first all UEs that are currently connected, and then all DUs, in
+order.
+
+For each UE, it prints:
+
+```
+UE 0 CU UE ID 1 DU UE ID 40352 RNTI 9da0 random identity c0f1ac9824000000:
+ last RRC activity: 5 seconds ago
+ PDU session 0 ID 10 status established
+ associated DU: DU assoc ID 8
+```
+
+where `UE 0` is the UE index, CU UE ID and DU UE IDs are the IDs used to
+exchange over F1 (cf. scheduler logs). Further, it shows RNTI, when the last
+RRC activity happened, the status of PDU sessions and which DU is associated
+(through the SCTP association ID).
+
+
+For each DU, it prints:
+
+```
+1 connected DUs
+[1] DU ID 3584 (gNB-OAI-DU) assoc_id 8: nrCellID 12345678, PCI 0, SSB ARFCN 641280
+ TDD: band 78 ARFCN 640008 SCS 30 (kHz) PRB 106
+```
+
+i.e., an index (`[1]`), the DU ID and it's name, the SCTP association ID
+(`assoc_id 8`, cf. UE information), and DU specific information for the cell
+(cell ID, physical cell identity/PCI, the SSB frequency in ARFCN notation, the
+band and Point A ARFCN, subcarrier spacing/SCS, and the number of resource
+blocks/PRB). Only one cell per DU is supported.
+
+As of now, it does not print information about connected CU-UPs or AMFs.
+
+# Configuration of the RRC
+
+## Split-related options (when running in a CU or CU-CP)
+
+See [F1 documentation](../F1-design.md) for information about the F1 split.
+See [E1 documentation](../E1AP/E1-design.md) for information about the E1 split.
+
+## RRC-specific configuration options
+
+In the `gNBs` section of the gNB/CU/CU-CP configuration file is the
+RRC-specific configuration
+
+### cell-specific options
+
+- `gNB_ID` and `gNB_name`: ID and name of the gNB
+- `tracking_area_code`: the current tracking area code in the range `[0x0001,
+ 0xfffd]`
+- `plmn`: the PLMN, which is a list of entries consisting of:
+ - `mcc`: mobile country code
+ - `mnc`: mobile network code
+ - `mnc_length`: length of mobile network code, allowed values: 2, 3
+ - `snssaiList`: list of NSSAI (network selection slice assistence
+ information, "slice ID"), which itself consists in
+ - `sst`: slice service type, in `[1,255]`
+ - `sd` (default `0xffffff`): slice differentiator, in `[0,0xffffff]`,
+ `0xffffff` is a reserved value and means "no SD"
+ Note that: SST=1, no SD is "eMBB"; SST=2, no SD is "URLLC"; SST=3, no SD
+ is "mMTC"
+- `enable_sdap` (default: false): enable the use of the SDAP layer. If
+ deactivated, a transparent SDAP header is prepended to packets, but no
+ further processing is being done.
+
+### UE-specific configuration
+
+- `um_on_default_drb` (default: false): use RLC UM instead of RLC AM on default
+ bearers
+- `drbs` (default: 0): the number of DRBs to allocate for a UE, only useful for
+ do-ra or phy-test testing
+
+### Neighbor-gNB configuration
+
+ TBD
+
+Refer to the [handover tutorial](../handover-tutorial.md) for more information.
diff --git a/doc/handover-tutorial.md b/doc/handover-tutorial.md
new file mode 100644
index 0000000000..8595fe05d4
--- /dev/null
+++ b/doc/handover-tutorial.md
@@ -0,0 +1,329 @@
+This tutorial explains how to perform handovers. For the moment, only F1
+handovers are supported.
+
+[[_TOC_]]
+
+# Considered setup for F1 handover
+
+We consider one CU and two DUs, connected over F1. The UE is initially
+connected over the radio interface ("Uu") to DU0. Via movement to a new
+position ("new pos"), it will trigger an event such that the CU triggers a
+handover of the UE from DU0 to DU1. Alternatively, a manual trigger can do the
+same.
+
+
+
+# Steps to run F1 handover with OAI UE
+
+Measurement reporting and processing of RRC Reconfiguration for Mobility are
+not completed at the UE. Nevertheless, it is possible to make simple handover
+tests without any radio setup, on a single PC, with the OAI UE, in RFsimulator.
+
+## Build with telnet support
+
+Since the UE does not support any measurement reporting, it cannot trigger a
+handover on its own; it has to be triggered manually through telnet. Thus,
+build both gNB and UE as well as activate the build of telnet to that purpose:
+
+ ./build_oai --ninja --nrUE --gNB --build-lib telnetsrv
+
+## Run the setup
+
+This tutorial assumes you have a core network running; [refer to the
+corresponding tutorial](./NR_SA_Tutorial_OAI_CN5G.md) if this is not the case
+yet.
+
+We will use the TDD configuration files in the repository for the
+[CU](../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-cu.sa.f1.conf) as well for
+[DU0](../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band78.106prb.rfsim.pci0.conf)
+and
+[DU1](../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band78.106prb.rfsim.pci1.conf).
+Note how the DUs differ in their DU ID (for identification at the CU), nr
+cellid (global identification), physical cell ID (identification through UE),
+frequency (limitation at OAI UE), and IP address.
+
+Make sure that using RFsimulator and the CU and each DU, you can achieve a full
+connection of the UE (independently, i.e., running one DU a time). Once this is
+done, follow below steps to trigger a handover:
+
+Start the CU including telnet support:
+
+ sudo ./nr-softmodem --sa -O ../../../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-cu.sa.f1.conf --telnetsrv --telnetsrv.shrmod ci
+
+Start DU0:
+
+ sudo ./nr-softmodem --sa --rfsim -O ../../../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band78.106prb.rfsim.pci0.conf --rfsimulator.serveraddr 127.0.0.1
+
+Start the UE, and let it connect completely:
+
+ sudo ./nr-uesoftmodem --sa -C 3450720000 -r 106 --numerology 1 --ssb 516 -O --rfsim --rfsimulator.serveraddr server
+
+Note how the RFsimulator roles have been switched, and RFsim server is at the
+UE side; _this is important_. Replace `` with the UE configuration
+matching your core. If you followed the CN and oaiUE tutorials, you can remove
+`-O ` and replace it with `--uicc0.imsi 001010000000001`.
+
+Once the UE is connected, start DU1:
+
+ sudo ./nr-softmodem --sa --rfsim -O ../../../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band78.106prb.rfsim.pci1.conf --rfsimulator.serveraddr 127.0.0.1
+
+Once DU1 is online, you can trigger a handover by issuing this command
+
+ echo ci trigger_f1_ho | nc 127.0.0.1 9090 && echo
+
+You should see how the UE switches from one DU to another. See additional
+information further below.
+
+A number of remarks:
+
+1. It is important that you start DU0, UE, DU1 in order, and having UE connect
+ to DU0 before starting DU1. This is because we don't employ any channel
+ emulation, and the UE could not decode the SIB1 of DU0 to connect.
+1. The RFsimulator roles are switched. Typically, the gNB RFsim acts as the
+ server and the UE as a client. However, RFsim is limited to one server with
+ multiple clients. Since the UE should be able to connect to both DUs, it has
+ to act as the server, and both DUs are a client.
+1. If you see errors `could not open a socket` and/or `Could not start the RF
+ device`, this means that RFsim could not be started. Handover will not work;
+ please refer to the preceding point to fix this (i.e., run the UE as the
+ RFsim server).
+1. In some cases, if the RFsim server is at the UE, the whole system can block;
+ in this case, stop UE and all DUs and restart (the CU can keep running).
+
+## Additional information to the manual HO trigger
+
+You can trigger the handover manually by logging in through telnet:
+
+ telnet 127.0.0.1 9090
+
+and then manually typing `ci trigger_f1_ho`.
+
+The command using `nc` (`netcat`) above triggers handovers directly from bash.
+It does the same as logging in through telnet otherwise, and is a shorthand.
+
+The full command is
+
+ ci trigger_f1_ho [cu-ue-id]
+
+`cu-ue-id` is optional. If only one UE context is present in the RRC, it will
+trigger the handover for this UE in a round-robin fashion across all DUs. If
+there is only one DU, the handover request will be rejected. Similarly, if
+there are multiple UE contexts present at RRC, the handover will be rejected,
+and you have to manually type the CU UE ID. You can see a list of all UEs in
+the file `nrRRC_stats.log` that is printed periodically in the working
+directory of the CU.
+
+# Steps to run F1 handover with COTS UE
+
+You can do handover across DUs with a COTS UE. Note that these DUs should be
+separated by at least multiple meters to ensure that the UE will receive
+different signal strengths when moving between cells.
+
+We only support intra-frequency handovers yet. We have verified with USRPs
+only, although other radios should work as well.
+
+For UEs, we verified Quectel modules and iPhones. Note, though, that not all
+phones might work; for instance, we did not achieve handovers with a OnePlus
+Nord, yet.
+
+## Steps
+
+First, make sure that you can run both DUs with the CU independently. Use the
+same radio hardware for both radios to ensure that both cells can be received
+equally good by the UE.
+
+In order to enable handovers (triggered by the UE), you have to configure the
+neighbour relation of the DUs at the CU. To do so, proceed as follows:
+
+1. To simplify filling the right values in the neighbour configuration, you can
+ rely on the information the CU has about both DUs. Start the CU and both
+ DUs. Navigate to the directory from which you started the CU, and print RRC
+ statistics:
+ ```
+ cat nrRRC_stats.log
+ ```
+1. Fill in the `neighbour-config.conf` configuration file as shown below, and
+ `@include` it in the CU file.
+1. Start the CU and both DUs.
+1. Bring the phone close to one cell, and leave flight mode. It should connect
+ to the DU to which it is closer.
+1. Move the UE towards the other DU; it should trigger an "A3 event" (Neighbour
+ Becomes Better than Serving), and the CU will trigger the handover to the
+ other DU.
+
+The output on the terminal should be the same as with RFsim. If no handover is
+triggered:
+
+- Make sure that both DUs use the same hardware.
+- Make sure that the UE sees both cells. For instance, you can switch to flight
+ mode, go closer to the other DU, and switch off flight mode -- the UE should
+ connect to that second UE.
+- We did not manage handover with every phone yet -- make sure you use one of
+ the list provided above.
+
+You can also force a handover through telnet as described above. (In fact, the
+decision about a handover is always at the network-side, the UE only "assists"
+through measurements telling the CU that one DU is stronger than others.
+Hence, "forcing" a handover just means that you manually trigger the handover,
+instead of waiting for UE measurement report.)
+
+## Example neighbour configuration
+
+Below is an example neighbour configuration. It is based on this DU information
+gathered from `nrRRC_stats.log` at the CU:
+
+```
+[1] DU ID 3585 (gNB-in-docker) assoc_id 4161: nrCellID 11111111, PCI 1, SSB ARFCN 643296
+ TDD: band 78 ARFCN 642024 SCS 30 (kHz) PRB 106
+[2] DU ID 3584 (gNB-in-docker) assoc_id 4163: nrCellID 12345678, PCI 0, SSB ARFCN 643296
+ TDD: band 78 ARFCN 642024 SCS 30 (kHz) PRB 106
+```
+
+Note how both DUs have one cell on the same frequency and the same radio
+configuration. From this, fill the neighbour list as shown below.
+
+Concretely, the first cell is `12345678` (on DU `[2]`), and it has `11111111`
+(on DU `[1]`) as its neighbour; hence in the first block, you fill
+`physical_cellId` and other values for DU `[1]`, and vice versa.
+
+The below configuration further enables periodic measurements, A2 event
+("Serving becomes worse than threshold"), and A3 events ("Neighbour Becomes
+Better than Serving"). The A2 event can be disabled by setting `enable = 0`. A3
+events cannot be disabled as of now. Further, the A3 events can be made
+specific to cells; `cell_id = -1` means "any cell".
+
+```
+neighbour_list = (
+ {
+ nr_cellid = 12345678;
+ neighbour_cell_configuration = (
+ {
+ gNB_ID = 0xe01;
+ nr_cellid = 11111111;
+ physical_cellId = 1;
+ absoluteFrequencySSB = 643296;
+ subcarrierSpacing = 1; #30 KHz
+ plmn = { mcc = 001; mnc = 01; mnc_length = 2};
+ tracking_area_code = 1;
+ }
+ )
+ },
+ {
+ nr_cellid = 11111111;
+ neighbour_cell_configuration = (
+ {
+ gNB_ID = 0xe00;
+ nr_cellid = 12345678;
+ physical_cellId = 0;
+ absoluteFrequencySSB = 643296;
+ subcarrierSpacing = 1; #30 KHz
+ plmn = { mcc = 001; mnc = 01; mnc_length = 2};
+ tracking_area_code = 1;
+ }
+ )
+ }
+ );
+
+
+nr_measurement_configuration = {
+ Periodical = {
+ enable = 1;
+ includeBeamMeasurements = 1;
+ maxNrofRS_IndexesToReport = 4;
+ };
+
+ A2 = {
+ enable = 1;
+ threshold = 60;
+ timeToTrigger = 1;
+ };
+
+ A3 = ({
+ cell_id = -1; #Default
+ offset = 10;
+ hysteresis = 0;
+ timeToTrigger = 1
+ })
+};
+```
+
+# Handovers triggers and NTN
+
+Typically, in terrestrial networks, channel measurements as well as criteria
+such as load in base stations, is used to determine when and where to handover
+a UE.
+
+## NTN
+
+Doppler spreading and time selectivity of the channel are already a challenge
+for conventional terrestrial networks. However, in the context of
+non-terrestrial networks (NTN), and low-earth orbit (LEO) systems, the
+satellites can have speeds up 7.56 km/s, which is much faster than 0.14 km/s of
+a high-speed train in terrestrial networks. Also, the delays in LEO are more varied
+and longer, and the path loss is larger, because the communication distances is up
+to 10 times longer than in terrestrial networks. Moreover, downlink in LEO
+presents a high interference from adjacent satellite beams, and all these
+features contribute to reduced received signal strength variation in these
+networks compared to terrestrial networks. Typically, the criteria used in
+algorithms presented in conventional terrestrial networks to trigger a handover
+from a gNB to another one is based on signal strength measurements (cf., A3
+event above). However, the reduced received signal strength variation in NTNs
+make these algorithms inefficient for LEO systems. Therefore, algorithms with
+criteria for handover triggering that address the specifics of LEO systems are
+crucial for an efficient handover processing that ensures a robust
+communication with low dropping probability. Some criteria for handovers in NTN
+are as follows:
+
+- **Measurement-based triggering**: This method is based on signal strength
+ measurement, and as stated above, it may not be efficient. The triggering
+ thresholds and which measurement events to use as triggers, as reference
+ signal received power (RSRP), reference signal received quality (RSRQ), or
+ received signal strength indicator (RSSI), should be configured. This method
+ relies on UE estimates and established channel estimation techniques, however
+ it would require neighbouring cell lists which can be hard because the
+ fast-moving of satellites leads to a fast cell coverage deviation.
+
+- **Location-based triggering**: This method is based on UE and satellite
+ location, which can be applied jointly (or not) with another trigger as the
+ measurement-based trigger. For instance, for a deterministic satellite
+ movement, it is possible to predict the configure triggering condition, and
+ the initial association of the UE can be performed based on the distance with
+ the nearest satellite, because we can know the location of the UE and NTN
+ satellite to compute the distance.
+
+- **Elevation angles of source and target cells based triggering**: This method
+ is similar to the previous one, but it is based on the largest elevation
+ angle.
+
+- **Time/timer-based triggering**: This method uses triggering conditions based
+ on UTC time or a timer-based solution, which can also be applied jointly (or
+ not) with another trigger as the measurement-based trigger. The timer-based
+ handover trigger considers the deterministic satellite movement to predict
+ the time duration for which the satelliteās footprint covers a certain zone.
+
+- **Timing advance value-based triggering**: This method uses the timing
+ advance value (independently or jointly with another trigger) to trigger a
+ handover to the target cell. It is appropriate to overcome the Random-Access
+ preamble reception issue, where the UE needs to pre-compensate the instant
+ which sends the preamble. However, UEs with GNSS support are required to
+ perform this method.
+
+## Simple location/time-based trigger
+
+A location-based handover trigger, somewhat aligned with 3GPP Rel.17, taking
+advantage of deterministic satellite movement, can be implemented whereby it is
+assumed that the locations of the UE and the NTN satellite are known. From a
+practical point of view, this means that we know how long it takes to perform
+the handover, and therefore we only need to implement a timer.
+
+It is possible to modify the source code to trigger a time-based handover, or
+combine with other methods. If all what is required is a trigger every 15
+seconds, you can also resort to the telnet-based handover trigger above, and
+run in a terminal:
+
+```bash
+while true; do
+ echo ci trigger_f1_ho | nc -N 127.0.0.1 9090 && echo
+ sleep 15
+done
+```
diff --git a/executables/nr-ue.c b/executables/nr-ue.c
index 01f18dc38d..a06c13a59a 100644
--- a/executables/nr-ue.c
+++ b/executables/nr-ue.c
@@ -38,6 +38,7 @@
#include "LAYER2/nr_rlc/nr_rlc_oai_api.h"
#include "RRC/NR/MESSAGES/asn1_msg.h"
#include "openair1/PHY/TOOLS/phy_scope_interface.h"
+#include "PHY/MODULATION/nr_modulation.h"
/*
* NR SLOT PROCESSING SEQUENCE
@@ -604,9 +605,23 @@ static int UE_dl_preprocessing(PHY_VARS_NR_UE *UE, const UE_nr_rxtx_proc_t *proc
// Start synchronization with a target gNB
if (UE->synch_request.received_synch_request == 1) {
+ fapi_nr_synch_request_t *synch_req = &UE->synch_request.synch_req;
UE->is_synchronized = 0;
- UE->UE_scan_carrier = UE->synch_request.synch_req.ssb_bw_scan;
- UE->target_Nid_cell = UE->synch_request.synch_req.target_Nid_cell;
+ UE->UE_scan_carrier = synch_req->ssb_bw_scan;
+ UE->target_Nid_cell = synch_req->target_Nid_cell;
+
+ uint64_t dl_bw = (12 * fp->N_RB_DL) * (15000 << fp->numerology_index);
+ uint64_t dl_CarrierFreq = (dl_bw >> 1) + (uint64_t)UE->nrUE_config.carrier_config.dl_frequency * 1000;
+ uint64_t ul_bw = (12 * fp->N_RB_UL) * (15000 << fp->numerology_index);
+ uint64_t ul_CarrierFreq = (ul_bw >> 1) + (uint64_t)UE->nrUE_config.carrier_config.uplink_frequency * 1000;
+ if (dl_CarrierFreq != fp->dl_CarrierFreq || ul_CarrierFreq != fp->ul_CarrierFreq) {
+ fp->dl_CarrierFreq = dl_CarrierFreq;
+ fp->ul_CarrierFreq = ul_CarrierFreq;
+ nr_rf_card_config_freq(&openair0_cfg[UE->rf_map.card], ul_CarrierFreq, dl_CarrierFreq, 0);
+ UE->rfdevice.trx_set_freq_func(&UE->rfdevice, &openair0_cfg[0]);
+ init_symbol_rotation(fp);
+ }
+
clean_UE_harq(UE);
UE->synch_request.received_synch_request = 0;
}
diff --git a/executables/nr-uesoftmodem.c b/executables/nr-uesoftmodem.c
index f665dd2673..4e5e1a19f9 100644
--- a/executables/nr-uesoftmodem.c
+++ b/executables/nr-uesoftmodem.c
@@ -84,7 +84,7 @@ unsigned short config_frames[4] = {2,9,11,13};
#include "executables/softmodem-common.h"
#include "executables/thread-common.h"
-#include "nr_nas_msg_sim.h"
+#include "nr_nas_msg.h"
#include
#include "openair2/GNB_APP/gnb_paramdef.h"
@@ -335,7 +335,7 @@ static void trigger_stop(int sig)
static void trigger_deregistration(int sig)
{
if (!stop_immediately) {
- MessageDef *msg = itti_alloc_new_message(TASK_RRC_UE_SIM, 0, NAS_DEREGISTRATION_REQ);
+ MessageDef *msg = itti_alloc_new_message(TASK_NAS_NRUE, 0, NAS_DEREGISTRATION_REQ);
NAS_DEREGISTRATION_REQ(msg).cause = AS_DETACH;
itti_send_msg_to_task(TASK_NAS_NRUE, 0, msg);
stop_immediately = true;
diff --git a/openair1/PHY/CODING/viterbi_lte.c b/openair1/PHY/CODING/viterbi_lte.c
index d3fd71bc13..4efab84f49 100644
--- a/openair1/PHY/CODING/viterbi_lte.c
+++ b/openair1/PHY/CODING/viterbi_lte.c
@@ -340,7 +340,7 @@ int test_viterbi(uint8_t dabflag)
int main(int argc, char **argv)
{
- char c;
+ int c;
uint8_t dabflag=0;
while ((c = getopt (argc, argv, "d")) != -1) {
diff --git a/openair1/PHY/NR_TRANSPORT/nr_dlsch.c b/openair1/PHY/NR_TRANSPORT/nr_dlsch.c
index a1b9b5b283..321ff3028b 100644
--- a/openair1/PHY/NR_TRANSPORT/nr_dlsch.c
+++ b/openair1/PHY/NR_TRANSPORT/nr_dlsch.c
@@ -250,8 +250,8 @@ void nr_generate_pdsch(processingData_L1tx_t *msgTx, int frame, int slot)
NR_DL_FRAME_PARMS *fp = &gNB->frame_parms;
const uint32_t *gold =
nr_gold_pdsch(fp->N_RB_DL, fp->symbols_per_slot, rel15->dlDmrsScramblingId, rel15->SCID, slot, l_symbol);
- nr_modulation(gold, n_dmrs * DMRS_MOD_ORDER, DMRS_MOD_ORDER,
- (int16_t *)mod_dmrs); // Qm = 2 as DMRS is QPSK modulated
+ // Qm = 2 as DMRS is QPSK modulated
+ nr_modulation(gold, n_dmrs * DMRS_MOD_ORDER, DMRS_MOD_ORDER, (int16_t *)mod_dmrs);
#ifdef DEBUG_DLSCH
printf("DMRS modulation (symbol %d, %d symbols, type %d):\n", l_symbol, n_dmrs, dmrs_Type);
diff --git a/openair1/PHY/TOOLS/smbv.c b/openair1/PHY/TOOLS/smbv.c
index d361872547..a1088cc705 100644
--- a/openair1/PHY/TOOLS/smbv.c
+++ b/openair1/PHY/TOOLS/smbv.c
@@ -567,7 +567,7 @@ void help (void)
int main(int argc,char **argv)
{
- char c,smbv_ip[16],smbv_fname[256];
+ char smbv_ip[16], smbv_fname[256];
extern char *optarg;
int i;
@@ -575,6 +575,7 @@ int main(int argc,char **argv)
strcpy(smbv_ip,DEFAULT_SMBV_IP);
strcpy(smbv_fname,DEFAULT_SMBV_FNAME);
+ int c;
while ((c = getopt (argc, argv, "hW:")) != -1) {
switch (c) {
case 'W':
diff --git a/openair1/SIMULATION/LTE_PHY/framegen.c b/openair1/SIMULATION/LTE_PHY/framegen.c
index 521df12ca1..fe1c4b49b7 100644
--- a/openair1/SIMULATION/LTE_PHY/framegen.c
+++ b/openair1/SIMULATION/LTE_PHY/framegen.c
@@ -461,9 +461,6 @@ void lte_param_init( unsigned char transmission_mode,
int main(int argc, char **argv)
{
-
- char c;
-
int i,l,aa,aarx,sector;
unsigned char pbch_pdu[6];
@@ -504,6 +501,7 @@ int main(int argc, char **argv)
logInit();
g_log->log_component[PHY].level = LOG_DEBUG;
+ int c;
while ((c = getopt (argc, argv, "hpFb:n:m:x:N:R:")) != -1) {
switch (c) {
case 'n':
diff --git a/openair1/SIMULATION/LTE_PHY/pbchsim.c b/openair1/SIMULATION/LTE_PHY/pbchsim.c
index 799008c121..393d5778d6 100644
--- a/openair1/SIMULATION/LTE_PHY/pbchsim.c
+++ b/openair1/SIMULATION/LTE_PHY/pbchsim.c
@@ -57,7 +57,6 @@ int32_t dummy3[2048*14];
int main(int argc, char **argv) {
- char c;
int i,l,aa;
double sigma2, sigma2_dB=0,SNR,snr0=-2.0,snr1;
uint8_t snr1set=0;
@@ -104,6 +103,7 @@ int main(int argc, char **argv) {
logInit();
number_of_cards = 1;
+ int c;
while ((c = getopt (argc, argv, "f:hA:pf:g:i:j:n:s:S:t:x:y:z:N:F:GR:O:dP:")) != -1) {
switch (c) {
case 'f':
diff --git a/openair1/SIMULATION/LTE_PHY/pdcchsim.c b/openair1/SIMULATION/LTE_PHY/pdcchsim.c
index 540911134d..14587d2a5d 100644
--- a/openair1/SIMULATION/LTE_PHY/pdcchsim.c
+++ b/openair1/SIMULATION/LTE_PHY/pdcchsim.c
@@ -374,7 +374,6 @@ DCI_PDU *get_dci(LTE_DL_FRAME_PARMS *lte_frame_parms,uint8_t log2L, uint8_t log2
extern int QPSK[4],QPSK2[4];
int main(int argc, char **argv) {
- char c;
int i,l,aa;
double sigma2, sigma2_dB=0,SNR,snr0=-2.0,snr1;
int **txdata;
@@ -425,6 +424,7 @@ int main(int argc, char **argv) {
cpuf = get_cpu_freq_GHz();
logInit();
+ int c;
while ((c = getopt (argc, argv, "hapFg:R:c:n:s:x:y:z:L:M:N:I:f:i:S:P:Y")) != -1) {
switch (c) {
case 'a':
diff --git a/openair1/SIMULATION/LTE_PHY/prachsim.c b/openair1/SIMULATION/LTE_PHY/prachsim.c
index 5b7c6ddeff..1b7f9e80e4 100644
--- a/openair1/SIMULATION/LTE_PHY/prachsim.c
+++ b/openair1/SIMULATION/LTE_PHY/prachsim.c
@@ -45,7 +45,6 @@ double cpuf;
void dump_prach_config(LTE_DL_FRAME_PARMS *frame_parms,uint8_t subframe);
int main(int argc, char **argv) {
- char c;
int i,aa,aarx;
double sigma2, sigma2_dB=0,SNR,snr0=-2.0,snr1=0.0,ue_speed0=0.0,ue_speed1=0.0;
uint8_t snr1set=0;
@@ -85,6 +84,7 @@ int main(int argc, char **argv) {
logInit();
number_of_cards = 1;
+ int c;
while ((c = getopt (argc, argv, "hHaA:Cr:p:g:n:s:S:t:x:y:v:V:z:N:F:d:Z:L:R:E")) != -1) {
switch (c) {
case 'a':
diff --git a/openair1/SIMULATION/LTE_PHY/pucchsim.c b/openair1/SIMULATION/LTE_PHY/pucchsim.c
index 2ca891f023..a65c681c07 100644
--- a/openair1/SIMULATION/LTE_PHY/pucchsim.c
+++ b/openair1/SIMULATION/LTE_PHY/pucchsim.c
@@ -45,7 +45,6 @@ PHY_VARS_UE *UE;
double cpuf;
int main(int argc, char **argv) {
- char c;
int i,l,aa;
double sigma2, sigma2_dB=0,SNR,snr0=-2.0,snr1=0.0;
uint8_t snr1set=0;
@@ -88,6 +87,7 @@ int main(int argc, char **argv) {
number_of_cards = 1;
cpuf = get_cpu_freq_GHz();
+ int c;
while ((c = getopt (argc, argv, "har:pf:g:n:s:S:x:y:z:N:F:T:R:")) != -1) {
switch (c) {
case 'a':
diff --git a/openair1/SIMULATION/LTE_PHY/scansim.c b/openair1/SIMULATION/LTE_PHY/scansim.c
index a473370f02..8921eaa680 100644
--- a/openair1/SIMULATION/LTE_PHY/scansim.c
+++ b/openair1/SIMULATION/LTE_PHY/scansim.c
@@ -57,7 +57,6 @@ mod_sym_t dummy3[2048*14];
int main(int argc, char **argv) {
- char c;
int i,l,aa;
double sigma2, sigma2_dB=0,SNR,snr0=-2.0,snr1;
uint8_t snr1set=0;
@@ -103,6 +102,7 @@ int main(int argc, char **argv) {
number_of_cards = 1;
openair_daq_vars.rx_rf_mode = 1;
+ int c;
while ((c = getopt (argc, argv, "f:hpf:g:n:s:S:t:x:y:z:N:F:GdP:")) != -1) {
switch (c) {
case 'f':
diff --git a/openair1/SIMULATION/LTE_PHY/syncsim.c b/openair1/SIMULATION/LTE_PHY/syncsim.c
index e8976ea898..ccdac230f8 100644
--- a/openair1/SIMULATION/LTE_PHY/syncsim.c
+++ b/openair1/SIMULATION/LTE_PHY/syncsim.c
@@ -304,7 +304,6 @@ void lte_param_init(unsigned char N_tx, unsigned char N_rx,unsigned char transmi
int main(int argc, char **argv) {
- char c;
int i,iout,l,aa,aarx;
double sigma2, sigma2_dB=0,SNR,snr0=10.0,snr1=11.0;
uint8_t snr1set=0;
@@ -377,6 +376,7 @@ int main(int argc, char **argv) {
rxdata[0] = (int *)malloc16(FRAME_LENGTH_BYTES);
rxdata[1] = (int *)malloc16(FRAME_LENGTH_BYTES);
*/
+ int c;
while ((c = getopt (argc, argv, "aehc:f:g:i:j:n:r:s:t:x:y:z:A:F:N:O:R:S:ZYDT:C:G:dB:U:u")) != -1) {
switch (c) {
case 'a':
diff --git a/openair1/SIMULATION/NR_PHY/dlschsim.c b/openair1/SIMULATION/NR_PHY/dlschsim.c
index b49eed1bd6..6aebb80dac 100644
--- a/openair1/SIMULATION/NR_PHY/dlschsim.c
+++ b/openair1/SIMULATION/NR_PHY/dlschsim.c
@@ -101,7 +101,6 @@ configmodule_interface_t *uniqCfg = NULL;
int main(int argc, char **argv)
{
- char c;
int i;
double SNR, SNR_lin, snr0 = -2.0, snr1 = 2.0;
double snr_step = 0.1;
@@ -146,6 +145,7 @@ int main(int argc, char **argv)
// logInit();
randominit(0);
+ int c;
while ((c = getopt(argc, argv, "--:O:df:hpVg:i:j:n:l:m:r:s:S:y:z:M:N:F:R:P:L:X:")) != -1) {
/* ignore long options starting with '--' and their arguments that are handled by configmodule */
diff --git a/openair1/SIMULATION/NR_PHY/pbchsim.c b/openair1/SIMULATION/NR_PHY/pbchsim.c
index f9b39de894..9f3f1760bd 100644
--- a/openair1/SIMULATION/NR_PHY/pbchsim.c
+++ b/openair1/SIMULATION/NR_PHY/pbchsim.c
@@ -177,7 +177,6 @@ void nr_phy_config_request_sim_pbchsim(PHY_VARS_gNB *gNB,
configmodule_interface_t *uniqCfg = NULL;
int main(int argc, char **argv)
{
- char c;
int i,aa,start_symbol;
double sigma2, sigma2_dB=10,SNR,snr0=-2.0,snr1=2.0;
double cfo=0;
@@ -246,6 +245,7 @@ int main(int argc, char **argv)
exit_fun("[NR_PBCHSIM] Error, configuration module init failed\n");
}
+ int c;
while ((c = getopt (argc, argv, "--:O:c:F:g:hIL:m:M:n:N:o:P:r:R:s:S:x:y:z:")) != -1) {
/* ignore long options starting with '--', option '-O' and their arguments that are handled by configmodule */
diff --git a/openair1/SIMULATION/NR_PHY/prachsim.c b/openair1/SIMULATION/NR_PHY/prachsim.c
index d4b109a529..4ccf0317e9 100644
--- a/openair1/SIMULATION/NR_PHY/prachsim.c
+++ b/openair1/SIMULATION/NR_PHY/prachsim.c
@@ -117,7 +117,6 @@ int NB_UE_INST = 1;
configmodule_interface_t *uniqCfg = NULL;
int main(int argc, char **argv){
- char c;
get_softmodem_params()->sl_mode = 0;
double sigma2, sigma2_dB = 0, SNR, snr0 = -2.0, snr1 = 0.0, ue_speed0 = 0.0, ue_speed1 = 0.0;
double **s_re, **s_im, **r_re, **r_im, iqim = 0.0, delay_avg = 0, ue_speed = 0, fs=-1, bw;
@@ -155,6 +154,7 @@ int main(int argc, char **argv){
randominit(0);
+ int c;
while ((c = getopt (argc, argv, "--:O:hHaA:Cc:l:r:p:g:m:n:s:S:t:x:y:v:V:z:N:F:d:Z:L:R:E")) != -1) {
/* ignore long options starting with '--', option '-O' and their arguments that are handled by configmodule */
diff --git a/openair1/SIMULATION/NR_PHY/psbchsim.c b/openair1/SIMULATION/NR_PHY/psbchsim.c
index 3f461e8666..972605fc96 100644
--- a/openair1/SIMULATION/NR_PHY/psbchsim.c
+++ b/openair1/SIMULATION/NR_PHY/psbchsim.c
@@ -315,7 +315,6 @@ double cpuf;
configmodule_interface_t *uniqCfg = NULL;
int main(int argc, char **argv)
{
- char c;
int test_freqdomain_loopback = 0, test_slss_search = 0;
int frame = 5, slot = 10, frame_tx = 0, slot_tx = 0;
int loglvl = OAILOG_INFO;
@@ -343,6 +342,7 @@ int main(int argc, char **argv)
randominit(0);
+ int c;
while ((c = getopt(argc, argv, "--:O:c:hn:o:s:FIL:N:R:S:T:")) != -1) {
/* ignore long options starting with '--', option '-O' and their arguments that are handled by configmodule */
diff --git a/openair1/SIMULATION/NR_PHY/pucchsim.c b/openair1/SIMULATION/NR_PHY/pucchsim.c
index 359310050c..45bc2aaa42 100644
--- a/openair1/SIMULATION/NR_PHY/pucchsim.c
+++ b/openair1/SIMULATION/NR_PHY/pucchsim.c
@@ -97,7 +97,6 @@ nrUE_params_t *get_nrUE_params(void) {
configmodule_interface_t *uniqCfg = NULL;
int main(int argc, char **argv)
{
- char c;
int i;//,l;
double sigma2, sigma2_dB=10,SNR,snr0=-2.0,snr1=2.0;
double cfo=0;
@@ -155,6 +154,7 @@ int main(int argc, char **argv)
randominit(0);
logInit();
+ int c;
while ((c = getopt (argc, argv, "--:O:f:hA:f:g:i:I:P:B:b:t:T:m:n:r:o:s:S:x:y:z:N:F:GR:IL:q:cd:")) != -1) {
/* ignore long options starting with '--', option '-O' and their arguments that are handled by configmodule */
diff --git a/openair1/SIMULATION/NR_PHY/ulschsim.c b/openair1/SIMULATION/NR_PHY/ulschsim.c
index ebe0ff8915..306e882ebf 100644
--- a/openair1/SIMULATION/NR_PHY/ulschsim.c
+++ b/openair1/SIMULATION/NR_PHY/ulschsim.c
@@ -121,7 +121,6 @@ nrUE_params_t *get_nrUE_params(void) {
configmodule_interface_t *uniqCfg = NULL;
int main(int argc, char **argv)
{
- char c;
int i;
double SNR, snr0 = -2.0, snr1 = 2.0, SNR_lin;
double snr_step = 0.1;
@@ -165,7 +164,7 @@ int main(int argc, char **argv)
//logInit();
randominit(0);
- //while ((c = getopt(argc, argv, "--:df:hpg:i:j:n:l:m:r:s:S:y:z:M:N:F:R:P:")) != -1) {
+ int c;
while ((c = getopt(argc, argv, "--:O:hg:n:s:S:py:z:M:N:R:F:m:l:q:r:W:")) != -1) {
/* ignore long options starting with '--', option '-O' and their arguments that are handled by configmodule */
diff --git a/openair1/SIMULATION/NR_PHY/ulsim.c b/openair1/SIMULATION/NR_PHY/ulsim.c
index 910d076e0f..431e4875fc 100644
--- a/openair1/SIMULATION/NR_PHY/ulsim.c
+++ b/openair1/SIMULATION/NR_PHY/ulsim.c
@@ -145,7 +145,6 @@ int main(int argc, char *argv[])
{
FILE *csv_file = NULL;
char *filename_csv = NULL;
- char c;
int i;
double SNR, snr0 = -2.0, snr1 = 2.0;
double sigma, sigma_dB;
@@ -224,6 +223,7 @@ int main(int argc, char *argv[])
/* initialize the sin-cos table */
InitSinLUT();
+ int c;
while ((c = getopt(argc, argv, "--:O:a:b:c:d:ef:g:h:i:k:m:n:op:q:r:s:t:u:v:w:y:z:C:F:G:H:I:M:N:PR:S:T:U:L:ZW:E:X:")) != -1) {
/* ignore long options starting with '--', option '-O' and their arguments that are handled by configmodule */
diff --git a/openair2/COMMON/f1ap_messages_types.h b/openair2/COMMON/f1ap_messages_types.h
index f159e63ca3..36c3ad4f76 100644
--- a/openair2/COMMON/f1ap_messages_types.h
+++ b/openair2/COMMON/f1ap_messages_types.h
@@ -442,6 +442,8 @@ typedef struct cu_to_du_rrc_information_s {
uint32_t uE_CapabilityRAT_ContainerList_length;
uint8_t * measConfig;
uint32_t measConfig_length;
+ uint8_t *handoverPreparationInfo;
+ uint32_t handoverPreparationInfo_length;
}cu_to_du_rrc_information_t;
typedef struct du_to_cu_rrc_information_s {
@@ -464,6 +466,11 @@ typedef enum ReconfigurationCompl_e {
RRCreconf_success = 2,
} ReconfigurationCompl_t;
+typedef enum TransmActionInd_e {
+ TransmActionInd_STOP,
+ TransmActionInd_RESTART,
+} TransmActionInd_t;
+
typedef struct f1ap_ue_context_setup_s {
uint32_t gNB_CU_ue_id;
uint32_t gNB_DU_ue_id;
@@ -478,6 +485,7 @@ typedef struct f1ap_ue_context_setup_s {
//uint8_t *du_to_cu_rrc_information;
du_to_cu_rrc_information_t *du_to_cu_rrc_information;
uint32_t du_to_cu_rrc_information_length;
+ uint16_t *crnti;
f1ap_drb_to_be_setup_t *drbs_to_be_setup;
uint8_t drbs_to_be_setup_length;
f1ap_drb_to_be_setup_t *drbs_to_be_modified;
@@ -494,6 +502,7 @@ typedef struct f1ap_ue_context_setup_s {
ReconfigurationCompl_t ReconfigComplOutcome;
uint8_t *rrc_container;
int rrc_container_length;
+ TransmActionInd_t *transm_action_ind;
} f1ap_ue_context_setup_t, f1ap_ue_context_modif_req_t, f1ap_ue_context_modif_resp_t;
typedef enum F1ap_Cause_e {
diff --git a/openair2/F1AP/f1ap_cu_task.c b/openair2/F1AP/f1ap_cu_task.c
index 72e3d86ea2..59e4bb29af 100644
--- a/openair2/F1AP/f1ap_cu_task.c
+++ b/openair2/F1AP/f1ap_cu_task.c
@@ -34,6 +34,7 @@
#include "f1ap_cu_interface_management.h"
#include "f1ap_cu_rrc_message_transfer.h"
#include "f1ap_cu_ue_context_management.h"
+#include "lib/f1ap_rrc_message_transfer.h"
#include "f1ap_cu_paging.h"
#include "f1ap_cu_task.h"
#include
@@ -174,7 +175,7 @@ void *F1AP_CU_task(void *arg) {
case F1AP_DL_RRC_MESSAGE: // from rrc
CU_send_DL_RRC_MESSAGE_TRANSFER(assoc_id,
&F1AP_DL_RRC_MESSAGE(received_msg));
- free(F1AP_DL_RRC_MESSAGE(received_msg).rrc_container);
+ free_dl_rrc_message_transfer(&F1AP_DL_RRC_MESSAGE(received_msg));
break;
case F1AP_UE_CONTEXT_SETUP_REQ: // from rrc
diff --git a/openair2/F1AP/f1ap_cu_ue_context_management.c b/openair2/F1AP/f1ap_cu_ue_context_management.c
index 65c34a10c1..f72ac3c588 100644
--- a/openair2/F1AP/f1ap_cu_ue_context_management.c
+++ b/openair2/F1AP/f1ap_cu_ue_context_management.c
@@ -276,6 +276,21 @@ int CU_send_UE_CONTEXT_SETUP_REQUEST(sctp_assoc_t assoc_id, f1ap_ue_context_setu
OCTET_STRING_fromBuf(measConfig, (const char*)f1ap_ue_context_setup_req->cu_to_du_rrc_information->measConfig,
f1ap_ue_context_setup_req->cu_to_du_rrc_information->measConfig_length);
}
+
+ /* optional */
+ /* HandoverPreparationInformation */
+ if (f1ap_ue_context_setup_req->cu_to_du_rrc_information->handoverPreparationInfo_length > 0) {
+ DevAssert(f1ap_ue_context_setup_req->cu_to_du_rrc_information->handoverPreparationInfo != NULL);
+ F1AP_ProtocolExtensionContainer_10696P60_t *p = calloc(1, sizeof(*p));
+ ie6->value.choice.CUtoDURRCInformation.iE_Extensions = (struct F1AP_ProtocolExtensionContainer *)p;
+ asn1cSequenceAdd(p->list, F1AP_CUtoDURRCInformation_ExtIEs_t, ie_ext);
+ ie_ext->id = F1AP_ProtocolIE_ID_id_HandoverPreparationInformation;
+ ie_ext->criticality = F1AP_Criticality_ignore;
+ ie_ext->extensionValue.present = F1AP_CUtoDURRCInformation_ExtIEs__extensionValue_PR_HandoverPreparationInformation;
+ OCTET_STRING_fromBuf(&ie_ext->extensionValue.choice.HandoverPreparationInformation,
+ (const char *)f1ap_ue_context_setup_req->cu_to_du_rrc_information->handoverPreparationInfo,
+ f1ap_ue_context_setup_req->cu_to_du_rrc_information->handoverPreparationInfo_length);
+ }
}
/* optional */
@@ -607,6 +622,12 @@ int CU_handle_UE_CONTEXT_SETUP_RESPONSE(instance_t instance, sctp_assoc_t assoc_
F1AP_ProtocolIE_ID_id_gNB_DU_UE_F1AP_ID, true);
f1ap_ue_context_setup_resp->gNB_DU_ue_id = ie->value.choice.GNB_DU_UE_F1AP_ID;
LOG_D(F1AP, "f1ap_ue_context_setup_resp->gNB_DU_ue_id is: %d \n", f1ap_ue_context_setup_resp->gNB_DU_ue_id);
+ F1AP_FIND_PROTOCOLIE_BY_ID(F1AP_UEContextSetupResponseIEs_t, ie, container, F1AP_ProtocolIE_ID_id_C_RNTI, false);
+ if (ie) {
+ f1ap_ue_context_setup_resp->crnti = calloc(1, sizeof(uint16_t));
+ *f1ap_ue_context_setup_resp->crnti = ie->value.choice.C_RNTI;
+ }
+
// DUtoCURRCInformation
F1AP_FIND_PROTOCOLIE_BY_ID(F1AP_UEContextSetupResponseIEs_t, ie, container,
F1AP_ProtocolIE_ID_id_DUtoCURRCInformation, true);
@@ -1008,12 +1029,15 @@ int CU_send_UE_CONTEXT_MODIFICATION_REQUEST(sctp_assoc_t assoc_id, f1ap_ue_conte
/* optional */
/* c7. TransmissionActionIndicator */
- if (0) {
+ if (f1ap_ue_context_modification_req->transm_action_ind != NULL) {
asn1cSequenceAdd(out->protocolIEs.list, F1AP_UEContextModificationRequestIEs_t, ie7);
ie7->id = F1AP_ProtocolIE_ID_id_TransmissionActionIndicator;
ie7->criticality = F1AP_Criticality_ignore;
ie7->value.present = F1AP_UEContextModificationRequestIEs__value_PR_TransmissionActionIndicator;
- ie7->value.choice.TransmissionActionIndicator = F1AP_TransmissionActionIndicator_stop;
+ ie7->value.choice.TransmissionActionIndicator = *f1ap_ue_context_modification_req->transm_action_ind;
+ // Stop is guaranteed to be 0, by restart might be different from 1
+ static_assert((int)F1AP_TransmissionActionIndicator_restart == (int)TransmActionInd_RESTART,
+ "mismatch of ASN.1 and internal representation\n");
}
/* optional */
diff --git a/openair2/F1AP/f1ap_du_interface_management.c b/openair2/F1AP/f1ap_du_interface_management.c
index 85e963ea6a..8f6114a9e5 100644
--- a/openair2/F1AP/f1ap_du_interface_management.c
+++ b/openair2/F1AP/f1ap_du_interface_management.c
@@ -186,6 +186,8 @@ int DU_handle_F1_SETUP_RESPONSE(instance_t instance, sctp_assoc_t assoc_id, uint
}
LOG_D(F1AP, "Sending F1AP_SETUP_RESP ITTI message\n");
f1_setup_response(&resp);
+ // free F1AP message after use
+ free_f1ap_setup_response(&resp);
return 0;
}
diff --git a/openair2/F1AP/f1ap_du_task.c b/openair2/F1AP/f1ap_du_task.c
index d221d3b35c..0d675a76e1 100644
--- a/openair2/F1AP/f1ap_du_task.c
+++ b/openair2/F1AP/f1ap_du_task.c
@@ -34,6 +34,7 @@
#include "f1ap_du_interface_management.h"
#include "f1ap_du_ue_context_management.h"
#include "f1ap_du_rrc_message_transfer.h"
+#include "lib/f1ap_rrc_message_transfer.h"
#include "f1ap_du_task.h"
#include
@@ -180,6 +181,7 @@ void *F1AP_DU_task(void *arg) {
case F1AP_UL_RRC_MESSAGE: // to rrc
DU_send_UL_NR_RRC_MESSAGE_TRANSFER(assoc_id, &F1AP_UL_RRC_MESSAGE(msg));
+ free_ul_rrc_message_transfer(&F1AP_UL_RRC_MESSAGE(msg));
break;
case F1AP_UE_CONTEXT_SETUP_RESP:
diff --git a/openair2/F1AP/f1ap_du_ue_context_management.c b/openair2/F1AP/f1ap_du_ue_context_management.c
index ad728d5c89..98de177af1 100644
--- a/openair2/F1AP/f1ap_du_ue_context_management.c
+++ b/openair2/F1AP/f1ap_du_ue_context_management.c
@@ -133,8 +133,13 @@ int DU_handle_UE_CONTEXT_SETUP_REQUEST(instance_t instance, sctp_assoc_t assoc_i
/* GNB_DU_UE_F1AP_ID */
F1AP_UEContextSetupRequestIEs_t *ieDU_UE;
F1AP_FIND_PROTOCOLIE_BY_ID(F1AP_UEContextSetupRequestIEs_t, ieDU_UE, container,
- F1AP_ProtocolIE_ID_id_gNB_DU_UE_F1AP_ID, true);
- f1ap_ue_context_setup_req->gNB_DU_ue_id = ieDU_UE->value.choice.GNB_DU_UE_F1AP_ID;
+ F1AP_ProtocolIE_ID_id_gNB_DU_UE_F1AP_ID, false);
+ // this should be optional. At present, the DU uses the RNTI, and hence, this
+ // value cannot exceed 0xffff (or something below, whatever is a valid
+ // C-RNTI). So we use a value > 0xffff to mark optionality
+ f1ap_ue_context_setup_req->gNB_DU_ue_id = -1; // 0xffffffff
+ if (ieDU_UE != NULL)
+ f1ap_ue_context_setup_req->gNB_DU_ue_id = ieDU_UE->value.choice.GNB_DU_UE_F1AP_ID;
/* SpCell_ID */
F1AP_UEContextSetupRequestIEs_t *ieNet;
@@ -171,12 +176,34 @@ int DU_handle_UE_CONTEXT_SETUP_REQUEST(instance_t instance, sctp_assoc_t assoc_i
F1AP_ProtocolIE_ID_id_CUtoDURRCInformation, false);
if(ieCuRrcInfo!=NULL){
f1ap_ue_context_setup_req->cu_to_du_rrc_information = (cu_to_du_rrc_information_t *)calloc(1,sizeof(cu_to_du_rrc_information_t));
- if(ieCuRrcInfo->value.choice.CUtoDURRCInformation.uE_CapabilityRAT_ContainerList!=NULL){
- f1ap_ue_context_setup_req->cu_to_du_rrc_information->uE_CapabilityRAT_ContainerList = (uint8_t *)calloc(1,ieCuRrcInfo->value.choice.CUtoDURRCInformation.uE_CapabilityRAT_ContainerList->size);
- memcpy(f1ap_ue_context_setup_req->cu_to_du_rrc_information->uE_CapabilityRAT_ContainerList, ieCuRrcInfo->value.choice.CUtoDURRCInformation.uE_CapabilityRAT_ContainerList->buf, ieCuRrcInfo->value.choice.CUtoDURRCInformation.uE_CapabilityRAT_ContainerList->size);
- f1ap_ue_context_setup_req->cu_to_du_rrc_information->uE_CapabilityRAT_ContainerList_length = ieCuRrcInfo->value.choice.CUtoDURRCInformation.uE_CapabilityRAT_ContainerList->size;
+ cu_to_du_rrc_information_t *cu2du = f1ap_ue_context_setup_req->cu_to_du_rrc_information;
+ const F1AP_CUtoDURRCInformation_t *cu2duie = &ieCuRrcInfo->value.choice.CUtoDURRCInformation;
+ if(cu2duie->uE_CapabilityRAT_ContainerList!=NULL){
+ cu2du->uE_CapabilityRAT_ContainerList = calloc(1, cu2duie->uE_CapabilityRAT_ContainerList->size);
+ memcpy(cu2du->uE_CapabilityRAT_ContainerList,
+ cu2duie->uE_CapabilityRAT_ContainerList->buf,
+ cu2duie->uE_CapabilityRAT_ContainerList->size);
+ cu2du->uE_CapabilityRAT_ContainerList_length = cu2duie->uE_CapabilityRAT_ContainerList->size;
LOG_D(F1AP, "Size f1ap_ue_context_setup_req->cu_to_du_rrc_information->uE_CapabilityRAT_ContainerList_length: %d \n", f1ap_ue_context_setup_req->cu_to_du_rrc_information->uE_CapabilityRAT_ContainerList_length);
}
+ if (cu2duie->iE_Extensions != NULL) {
+ const F1AP_ProtocolExtensionContainer_10696P60_t *ext = (const F1AP_ProtocolExtensionContainer_10696P60_t *)cu2duie->iE_Extensions;
+ for (int i = 0; i < ext->list.count; ++i) {
+ const F1AP_CUtoDURRCInformation_ExtIEs_t *cu2du_info = ext->list.array[i];
+ switch (cu2du_info->id) {
+ case F1AP_ProtocolIE_ID_id_HandoverPreparationInformation:
+ DevAssert(cu2du_info->extensionValue.present == F1AP_CUtoDURRCInformation_ExtIEs__extensionValue_PR_HandoverPreparationInformation);
+ const F1AP_HandoverPreparationInformation_t *hopi = &cu2du_info->extensionValue.choice.MeasurementTimingConfiguration;
+ cu2du->handoverPreparationInfo = calloc_or_fail(1, hopi->size);
+ memcpy(cu2du->handoverPreparationInfo, hopi->buf, hopi->size);
+ cu2du->handoverPreparationInfo_length = hopi->size;
+ break;
+ default:
+ LOG_W(F1AP, "unsupported CUtoDURRCInformation_ExtIE %ld encountered, ignoring\n", cu2du_info->id);
+ break;
+ }
+ }
+ }
}
/* DRB */
@@ -353,15 +380,12 @@ int DU_send_UE_CONTEXT_SETUP_RESPONSE(sctp_assoc_t assoc_id, f1ap_ue_context_set
/* optional */
/* c4. C_RNTI */
- if (0) {
+ if (resp->crnti!=NULL) {
asn1cSequenceAdd(out->protocolIEs.list, F1AP_UEContextSetupResponseIEs_t, ie4);
ie4->id = F1AP_ProtocolIE_ID_id_C_RNTI;
ie4->criticality = F1AP_Criticality_ignore;
ie4->value.present = F1AP_UEContextSetupResponseIEs__value_PR_C_RNTI;
- //C_RNTI_TO_BIT_STRING(rntiP, &ie->value.choice.C_RNTI);
- ie4->value.choice.C_RNTI=0;
- AssertFatal(false, "not implemented\n");
- LOG_E(F1AP,"RNTI to code!\n");
+ ie4->value.choice.C_RNTI = *resp->crnti;
}
/* optional */
@@ -889,6 +913,21 @@ int DU_handle_UE_CONTEXT_MODIFICATION_REQUEST(instance_t instance, sctp_assoc_t
F1AP_FIND_PROTOCOLIE_BY_ID(F1AP_UEContextModificationRequestIEs_t, ieSrb, container,
F1AP_ProtocolIE_ID_id_SRBs_ToBeSetupMod_List, false);
+ F1AP_UEContextModificationRequestIEs_t *ieTxInd;
+ F1AP_FIND_PROTOCOLIE_BY_ID(F1AP_UEContextModificationRequestIEs_t,
+ ieTxInd,
+ container,
+ F1AP_ProtocolIE_ID_id_TransmissionActionIndicator,
+ false);
+
+ if (ieTxInd != NULL) {
+ f1ap_ue_context_modification_req->transm_action_ind = calloc_or_fail(1, sizeof(*f1ap_ue_context_modification_req->transm_action_ind));
+ *f1ap_ue_context_modification_req->transm_action_ind = ieTxInd->value.choice.TransmissionActionIndicator;
+ // Stop is guaranteed to be 0, by restart might be different from 1
+ static_assert((int)F1AP_TransmissionActionIndicator_restart == (int)TransmActionInd_RESTART,
+ "mismatch of ASN.1 and internal representation\n");
+ }
+
if(ieSrb != NULL) {
f1ap_ue_context_modification_req->srbs_to_be_setup_length = ieSrb->value.choice.SRBs_ToBeSetupMod_List.list.count;
f1ap_ue_context_modification_req->srbs_to_be_setup = calloc(f1ap_ue_context_modification_req->srbs_to_be_setup_length,
diff --git a/openair2/F1AP/lib/f1ap_interface_management.c b/openair2/F1AP/lib/f1ap_interface_management.c
index cb0b6ab8aa..fe079faa4c 100644
--- a/openair2/F1AP/lib/f1ap_interface_management.c
+++ b/openair2/F1AP/lib/f1ap_interface_management.c
@@ -955,8 +955,7 @@ bool decode_f1ap_setup_failure(const F1AP_F1AP_PDU_t *pdu, f1ap_setup_failure_t
*/
bool eq_f1ap_setup_failure(const f1ap_setup_failure_t *a, const f1ap_setup_failure_t *b)
{
- if (a->transaction_id != b->transaction_id)
- return false;
+ _F1_EQ_CHECK_INT(a->transaction_id, b->transaction_id);
return true;
}
diff --git a/openair2/F1AP/lib/f1ap_lib_common.c b/openair2/F1AP/lib/f1ap_lib_common.c
index edf48f426c..2938898aff 100644
--- a/openair2/F1AP/lib/f1ap_lib_common.c
+++ b/openair2/F1AP/lib/f1ap_lib_common.c
@@ -37,32 +37,24 @@ bool eq_f1ap_plmn(const f1ap_plmn_t *a, const f1ap_plmn_t *b)
bool eq_f1ap_freq_info(const f1ap_nr_frequency_info_t *a, const f1ap_nr_frequency_info_t *b)
{
- if (a->arfcn != b->arfcn)
- return false;
- if (a->band != b->band)
- return false;
+ _F1_EQ_CHECK_INT(a->arfcn, b->arfcn);
+ _F1_EQ_CHECK_INT(a->band, b->band);
return true;
}
bool eq_f1ap_tx_bandwidth(const f1ap_transmission_bandwidth_t *a, const f1ap_transmission_bandwidth_t *b)
{
- if (a->nrb != b->nrb)
- return false;
- if (a->scs != b->scs)
- return false;
+ _F1_EQ_CHECK_INT(a->nrb, b->nrb);
+ _F1_EQ_CHECK_INT(a->scs, b->scs);
return true;
}
bool eq_f1ap_cell_info(const f1ap_served_cell_info_t *a, const f1ap_served_cell_info_t *b)
{
- if (a->nr_cellid != b->nr_cellid)
- return false;
- if (a->nr_pci != b->nr_pci)
- return false;
- if (*a->tac != *b->tac)
- return false;
- if (a->mode != b->mode)
- return false;
+ _F1_EQ_CHECK_LONG(a->nr_cellid, b->nr_cellid);
+ _F1_EQ_CHECK_INT(a->nr_pci, b->nr_pci);
+ _F1_EQ_CHECK_INT(*a->tac, *b->tac);
+ _F1_EQ_CHECK_INT(a->mode, b->mode);
if (a->mode == F1AP_MODE_TDD) {
/* TDD */
if (!eq_f1ap_tx_bandwidth(&a->tdd.tbw, &b->tdd.tbw))
@@ -80,10 +72,8 @@ bool eq_f1ap_cell_info(const f1ap_served_cell_info_t *a, const f1ap_served_cell_
if (!eq_f1ap_freq_info(&a->fdd.ul_freqinfo, &b->fdd.ul_freqinfo))
return false;
}
- if (a->measurement_timing_config_len != b->measurement_timing_config_len)
- return false;
- if (*a->measurement_timing_config != *b->measurement_timing_config)
- return false;
+ _F1_EQ_CHECK_INT(a->measurement_timing_config_len, b->measurement_timing_config_len);
+ _F1_EQ_CHECK_INT(*a->measurement_timing_config, *b->measurement_timing_config);
if (!eq_f1ap_plmn(&a->plmn, &b->plmn))
return false;
return true;
@@ -92,19 +82,13 @@ bool eq_f1ap_cell_info(const f1ap_served_cell_info_t *a, const f1ap_served_cell_
bool eq_f1ap_sys_info(const f1ap_gnb_du_system_info_t *a, const f1ap_gnb_du_system_info_t *b)
{
/* MIB */
- if (a->mib_length != b->mib_length)
- return false;
- for (int i = 0; i < a->mib_length; i++) {
- if (a->mib[i] != b->mib[i])
- return false;
- }
+ _F1_EQ_CHECK_INT(a->mib_length, b->mib_length);
+ for (int i = 0; i < a->mib_length; i++)
+ _F1_EQ_CHECK_INT(a->mib[i], b->mib[i]);
/* SIB1 */
- if (a->sib1_length != b->sib1_length)
- return false;
- for (int i = 0; i < a->sib1_length; i++) {
- if (a->sib1[i] != b->sib1[i])
- return false;
- }
+ _F1_EQ_CHECK_INT(a->sib1_length, b->sib1_length);
+ for (int i = 0; i < a->sib1_length; i++)
+ _F1_EQ_CHECK_INT(a->sib1[i], b->sib1[i]);
return true;
}
diff --git a/openair2/F1AP/tests/f1ap_lib_test.c b/openair2/F1AP/tests/f1ap_lib_test.c
index 969efc5afa..71d2bea953 100644
--- a/openair2/F1AP/tests/f1ap_lib_test.c
+++ b/openair2/F1AP/tests/f1ap_lib_test.c
@@ -1,3 +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
+ */
+
+/*! \file f1ap_lib_test.c
+ * \brief Test functions for F1AP encoding/decoding library
+ * \author Guido Casati, Robert Schmidt
+ * \date 2024
+ * \version 0.1
+ * \note
+ * \warning
+ */
+
#include
#include
#include
@@ -166,7 +196,7 @@ static void test_f1ap_setup_request(void)
/* gNB_DU_name */
uint8_t *gNB_DU_name = calloc(strlen("OAI DU") + 1, sizeof(uint8_t));
AssertFatal(gNB_DU_name != NULL, "out of memory\n");
- memcpy((void *)gNB_DU_name, "OAI DU", strlen("OAI DU") + 1);
+ memcpy(gNB_DU_name, "OAI DU", strlen("OAI DU") + 1);
/* sys_info */
uint8_t *mib = calloc(3, sizeof(uint8_t));
uint8_t *sib1 = calloc(3, sizeof(uint8_t));
diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c
index 9003cc42d2..26b2f35e0f 100644
--- a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c
+++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c
@@ -2240,7 +2240,7 @@ static int get_nr_prach_info_from_ssb_index(prach_association_pattern_t *prach_a
ssb_info_p->mapped_ro[n_mapped_ro]->frame,
ssb_info_p->mapped_ro[n_mapped_ro]->slot,
prach_assoc_pattern->nb_of_frame);
- if ((slot == ssb_info_p->mapped_ro[n_mapped_ro]->slot) &&
+ if ((slot == ssb_info_p->mapped_ro[n_mapped_ro]->slot) && prach_assoc_pattern->prach_conf_period_list[0].nb_of_frame != 0 &&
(ssb_info_p->mapped_ro[n_mapped_ro]->frame == (frame % prach_assoc_pattern->nb_of_frame))) {
uint8_t prach_config_period_nb = ssb_info_p->mapped_ro[n_mapped_ro]->frame / prach_assoc_pattern->prach_conf_period_list[0].nb_of_frame;
uint8_t frame_nb_in_prach_config_period = ssb_info_p->mapped_ro[n_mapped_ro]->frame % prach_assoc_pattern->prach_conf_period_list[0].nb_of_frame;
@@ -3450,7 +3450,7 @@ static bool fill_mac_sdu(NR_UE_MAC_INST_t *mac,
*pdu -= sh_size;
lcids_data_status[lc_idx] = false;
(*num_lcids_same_priority)--;
- LOG_D(NR_MAC, "No data to transmit for RB with LCID 0x%02x\n and hence set to false", lcid);
+ LOG_D(NR_MAC, "No data to transmit for RB with LCID 0x%02x and hence set to false\n", lcid);
return 0;
}
@@ -3608,7 +3608,11 @@ uint8_t nr_ue_get_sdu(NR_UE_MAC_INST_t *mac,
buflen_remain);
if (num_lcids_same_priority == count_same_priority_lcids) {
- buflen_ep = (buflen_remain - (count_same_priority_lcids * sh_size)) / count_same_priority_lcids;
+ const int32_t buflen_ep_tmp = (buflen_remain - (count_same_priority_lcids * sh_size)) / count_same_priority_lcids;
+ /* after serving equal priority LCIDs in the first round, buflen_remain could be > 0 and < (count_same_priority_lcids * sh_size)
+ if above division yeilds a remainder. hence the following sets buflen_ep to 0 if there is not enough buffer left for subsequent rounds
+ */
+ buflen_ep = buflen_ep_tmp < 0 ? 0 : buflen_ep_tmp;
}
while (buflen_remain > 0) {
diff --git a/openair2/LAYER2/NR_MAC_gNB/config.c b/openair2/LAYER2/NR_MAC_gNB/config.c
index bfbb5dcfb5..ea4fbed192 100644
--- a/openair2/LAYER2/NR_MAC_gNB/config.c
+++ b/openair2/LAYER2/NR_MAC_gNB/config.c
@@ -844,9 +844,9 @@ bool nr_mac_prepare_ra_ue(gNB_MAC_INST *nrmac, uint32_t rnti, NR_CellGroupConfig
/* Prepare a new CellGroupConfig to be applied for this UE. We cannot
* immediatly apply it, as we have to wait for the reconfiguration through RRC.
* This function sets up everything to apply the reconfiguration. Later, we
- * will trigger the timer with nr_mac_enable_ue_rrc_processing_timer(); upon
- * expiry nr_mac_apply_cellgroup() will apply the CellGroupConfig (radio config
- * etc). */
+ * will trigger the UE inactivity with nr_mac_interrupt_ue_transmission(); upon
+ * expiry, nr_mac_apply_cellgroup() will apply the CellGroupConfig (radio
+ * config etc). */
bool nr_mac_prepare_cellgroup_update(gNB_MAC_INST *nrmac, NR_UE_info_t *UE, NR_CellGroupConfig_t *CellGroup)
{
DevAssert(nrmac != NULL);
diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c
index d6dfa19dd4..3e738db42a 100644
--- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c
+++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c
@@ -40,7 +40,6 @@
#include "common/utils/LOG/vcd_signal_dumper.h"
#include "common/utils/nr/nr_common.h"
#include "UTIL/OPT/opt.h"
-#include "SIMULATION/TOOLS/sim.h" // for taus
/* rlc */
#include "openair2/LAYER2/nr_rlc/nr_rlc_oai_api.h"
@@ -677,16 +676,6 @@ static NR_RA_t *find_free_nr_RA(NR_RA_t *ra_base, int ra_count, uint16_t preambl
return NULL;
}
-static const NR_RA_t *find_nr_RA_rnti(const NR_RA_t *ra_base, int ra_count, rnti_t rnti)
-{
- for (int i = 0; i < ra_count; ++i) {
- const NR_RA_t *ra = &ra_base[i];
- if (ra->ra_state != nrRA_gNB_IDLE && ra->rnti == rnti)
- return ra;
- }
- return NULL;
-}
-
void nr_initiate_ra_proc(module_id_t module_idP,
int CC_id,
frame_t frameP,
@@ -712,23 +701,13 @@ void nr_initiate_ra_proc(module_id_t module_idP,
}
if (ra->rnti == 0) { // This condition allows for the usage of a preconfigured rnti for the CFRA
- int loop = 0;
- bool exist_connected_ue, exist_in_pending_ra_ue;
- rnti_t trial = 0;
- do {
- // 3GPP TS 38.321 version 15.13.0 Section 7.1 Table 7.1-1: RNTI values
- while (trial < 1 || trial > 0xffef)
- trial = (taus() % 0xffef) + 1;
- exist_connected_ue = find_nr_UE(&nr_mac->UE_info, trial) != NULL;
- exist_in_pending_ra_ue = find_nr_RA_rnti(cc->ra, sizeofArray(cc->ra), ra->rnti) != NULL;
- loop++;
- } while (loop < 100 && (exist_connected_ue || exist_in_pending_ra_ue) );
- if (loop == 100) {
+ bool rnti_found = nr_mac_get_new_rnti(&nr_mac->UE_info, cc->ra, sizeofArray(cc->ra), &ra->rnti);
+ if (!rnti_found) {
LOG_E(NR_MAC, "initialisation random access: no more available RNTIs for new UE\n");
+ nr_clear_ra_proc(ra);
NR_SCHED_UNLOCK(&nr_mac->sched_lock);
return;
}
- ra->rnti = trial;
}
ra->preamble_frame = frameP;
@@ -1698,12 +1677,9 @@ static void nr_generate_Msg2(module_id_t module_idP,
if (ra->cfra) {
NR_UE_info_t *UE = find_nr_UE(&RC.nrmac[module_idP]->UE_info, ra->rnti);
if (UE) {
- const NR_ServingCellConfig_t *servingCellConfig = UE->CellGroup ? UE->CellGroup->spCellConfig->spCellConfigDedicated : NULL;
- uint32_t delay_ms = servingCellConfig && servingCellConfig->downlinkBWP_ToAddModList
- ? NR_RRC_SETUP_DELAY_MS + NR_RRC_BWP_SWITCHING_DELAY_MS
- : NR_RRC_SETUP_DELAY_MS;
- NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl;
- sched_ctrl->rrc_processing_timer = (delay_ms << ra->DL_BWP.scs);
+ int delay = nr_mac_get_reconfig_delay_slots(ra->DL_BWP.scs);
+ interrupt_followup_action_t action = UE->reconfigCellGroup ? FOLLOW_INSYNC_RECONFIG : FOLLOW_INSYNC;
+ nr_mac_interrupt_ue_transmission(RC.nrmac[module_idP], UE, action, delay);
}
}
@@ -2248,8 +2224,9 @@ static void nr_generate_Msg4_MsgB(module_id_t module_idP,
// 3GPP TS 38.331 Section 12 Table 12.1-1: UE performance requirements for RRC procedures for UEs
// Msg4 may transmit a RRCReconfiguration, for example when UE sends RRCReestablishmentComplete and MAC CE for C-RNTI in Msg3.
// In that case, gNB will generate a RRCReconfiguration that will be transmitted in Msg4, so we need to apply CellGroup after the Ack,
- // UE->apply_cellgroup was already set when processing RRCReestablishment message
- nr_mac_enable_ue_rrc_processing_timer(nr_mac, UE, UE->apply_cellgroup);
+ // UE->reconfigCellGroup was already set when processing RRCReestablishment message
+ int delay = nr_mac_get_reconfig_delay_slots(UE->current_UL_BWP.scs);
+ nr_mac_interrupt_ue_transmission(nr_mac, UE, UE->interrupt_action, delay);
nr_clear_ra_proc(ra);
} else {
@@ -2291,8 +2268,9 @@ static void nr_check_Msg4_MsgB_Ack(module_id_t module_id, int CC_id, frame_t fra
// 3GPP TS 38.331 Section 12 Table 12.1-1: UE performance requirements for RRC procedures for UEs
// Msg4 may transmit a RRCReconfiguration, for example when UE sends RRCReestablishmentComplete and MAC CE for C-RNTI in Msg3.
// In that case, gNB will generate a RRCReconfiguration that will be transmitted in Msg4, so we need to apply CellGroup after the Ack,
- // UE->apply_cellgroup was already set when processing RRCReestablishment message
- nr_mac_enable_ue_rrc_processing_timer(RC.nrmac[module_id], UE, UE->apply_cellgroup);
+ // UE->reconfigCellGroup already set when processing RRCReestablishment message
+ int delay = nr_mac_get_reconfig_delay_slots(UE->current_UL_BWP.scs);
+ nr_mac_interrupt_ue_transmission(RC.nrmac[module_id], UE, UE->interrupt_action, delay);
nr_clear_ra_proc(ra);
if (sched_ctrl->retrans_dl_harq.head >= 0) {
@@ -2457,8 +2435,9 @@ void nr_schedule_RA(module_id_t module_idP,
ra->contention_resolution_timer--;
if (ra->contention_resolution_timer < 0) {
LOG_W(NR_MAC, "(%d.%d) RA Contention Resolution timer expired for UE 0x%04x, RA procedure failed...\n", frameP, slotP, ra->rnti);
- nr_mac_release_ue(mac, ra->rnti);
- nr_mac_trigger_release_complete(mac, ra->rnti);
+ bool requested = nr_mac_request_release_ue(mac, ra->rnti);
+ if (!requested)
+ nr_mac_release_ue(mac, ra->rnti);
nr_clear_ra_proc(ra);
continue;
}
diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c
index af2a2b24e7..82106d0ef2 100644
--- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c
+++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c
@@ -52,8 +52,8 @@
#define WORD 32
//#define SIZE_OF_POINTER sizeof (void *)
-int get_dl_tda(const gNB_MAC_INST *nrmac, const NR_ServingCellConfigCommon_t *scc, int slot) {
-
+int get_dl_tda(const gNB_MAC_INST *nrmac, const NR_ServingCellConfigCommon_t *scc, int slot)
+{
/* we assume that this function is mutex-protected from outside */
const NR_TDD_UL_DL_Pattern_t *tdd = scc->tdd_UL_DL_ConfigurationCommon ? &scc->tdd_UL_DL_ConfigurationCommon->pattern1 : NULL;
AssertFatal(tdd || nrmac->common_channels->frame_type == FDD, "Dynamic TDD not handled yet\n");
@@ -334,9 +334,8 @@ static void nr_store_dlsch_buffer(module_id_t module_id, frame_t frame, sub_fram
const int lcid = c->lcid;
const uint16_t rnti = UE->rnti;
LOG_D(NR_MAC, "In %s: UE %x: LCID %d\n", __FUNCTION__, rnti, lcid);
- if (lcid == DL_SCH_LCID_DTCH && sched_ctrl->rrc_processing_timer > 0) {
+ if (lcid == DL_SCH_LCID_DTCH && nr_timer_is_active(&sched_ctrl->transm_interrupt))
continue;
- }
start_meas(&RC.nrmac[module_id]->rlc_status_ind);
sched_ctrl->rlc_status[lcid] = mac_rlc_status_ind(module_id,
rnti,
@@ -390,10 +389,12 @@ void abort_nr_dl_harq(NR_UE_info_t* UE, int8_t harq_pid)
UE->mac_stats.dl.errors++;
}
-static void get_start_stop_allocation(gNB_MAC_INST *mac,
- NR_UE_info_t *UE,
- int *rbStart,
- int *rbStop)
+typedef struct {
+ int bwpStart;
+ int bwpSize;
+} dl_bwp_info_t;
+
+static dl_bwp_info_t get_bwp_start_size(gNB_MAC_INST *mac, NR_UE_info_t *UE)
{
NR_UE_DL_BWP_t *dl_bwp = &UE->current_DL_BWP;
NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl;
@@ -402,25 +403,28 @@ static void get_start_stop_allocation(gNB_MAC_INST *mac,
// in which case the size of CORESET 0 shall be used if CORESET 0 is configured for the cell
// and the size of initial DL bandwidth part shall be used if CORESET 0 is not configured for the cell.
// TS 38.214 Section 5.1.2.2.2
- *rbStop = dl_bwp->BWPSize - 1;
- *rbStart = 0; // start wrt BWPstart
+ dl_bwp_info_t bwp_info;
+ bwp_info.bwpSize = dl_bwp->BWPSize;
+ bwp_info.bwpStart = dl_bwp->BWPStart;
if (sched_ctrl->search_space->searchSpaceType->present == NR_SearchSpace__searchSpaceType_PR_common &&
dl_bwp->dci_format == NR_DL_DCI_FORMAT_1_0) {
if (mac->cset0_bwp_size != 0) {
- *rbStart = mac->cset0_bwp_start;
- *rbStop = *rbStart + mac->cset0_bwp_size - 1;
+ bwp_info.bwpStart = mac->cset0_bwp_start;
+ bwp_info.bwpSize = mac->cset0_bwp_size;
}
else {
- *rbStart = UE->sc_info.initial_dl_BWPStart;
- *rbStop = *rbStart + UE->sc_info.initial_dl_BWPSize - 1;
+ // TODO this is not entirely correct
+ // start would be the start of CORESET not of the initial BWP
+ bwp_info.bwpStart = UE->sc_info.initial_dl_BWPStart;
+ bwp_info.bwpSize = UE->sc_info.initial_dl_BWPSize;
}
}
+ return bwp_info;
}
static bool allocate_dl_retransmission(module_id_t module_id,
frame_t frame,
sub_frame_t slot,
- uint16_t *rballoc_mask,
int *n_rb_sched,
NR_UE_info_t *UE,
int current_harq_pid)
@@ -442,12 +446,6 @@ static bool allocate_dl_retransmission(module_id_t module_id,
int pm_index = (curInfo->nrOfLayers < retInfo->nrOfLayers) ? curInfo->pm_index : retInfo->pm_index;
const int coresetid = sched_ctrl->coreset->controlResourceSetId;
-
- int rbStop = 0;
- int rbStart = 0;
- get_start_stop_allocation(nr_mac, UE, &rbStart, &rbStop);
-
- int rbSize = 0;
const int tda = get_dl_tda(nr_mac, scc, slot);
AssertFatal(tda>=0,"Unable to find PDSCH time domain allocation in list\n");
@@ -470,6 +468,14 @@ static bool allocate_dl_retransmission(module_id_t module_id,
reuse_old_tda ? "reuse" : "do not reuse",
layers == retInfo->nrOfLayers ? "same" : "different");
+ // TODO assuming beam 0 for now
+ uint16_t *rballoc_mask = nr_mac->common_channels[CC_id].vrb_map[0];
+
+ dl_bwp_info_t bwp_info = get_bwp_start_size(nr_mac, UE);
+ int rbStart = bwp_info.bwpStart;
+ int rbStop = bwp_info.bwpStart + bwp_info.bwpSize - 1;
+ int rbSize = 0;
+
if (reuse_old_tda && layers == retInfo->nrOfLayers) {
/* Check that there are enough resources for retransmission */
while (rbSize < retInfo->rbSize) {
@@ -477,35 +483,27 @@ static bool allocate_dl_retransmission(module_id_t module_id,
rbSize = 0;
const uint16_t slbitmap = SL_to_bitmap(retInfo->tda_info.startSymbolIndex, retInfo->tda_info.nrOfSymbols);
- while (rbStart < rbStop && (rballoc_mask[rbStart] & slbitmap) != slbitmap)
+ while (rbStart < rbStop && (rballoc_mask[rbStart] & slbitmap))
rbStart++;
if (rbStart >= rbStop) {
- LOG_D(NR_MAC, "[UE %04x][%4d.%2d] could not allocate DL retransmission: no resources\n",
- UE->rnti,
- frame,
- slot);
+ LOG_D(NR_MAC, "[UE %04x][%4d.%2d] could not allocate DL retransmission: no resources\n", UE->rnti, frame, slot);
return false;
}
- while (rbStart + rbSize <= rbStop &&
- (rballoc_mask[rbStart + rbSize] & slbitmap) == slbitmap &&
- rbSize < retInfo->rbSize)
+ while (rbStart + rbSize <= rbStop && !(rballoc_mask[rbStart + rbSize] & slbitmap) && rbSize < retInfo->rbSize)
rbSize++;
}
} else {
/* the retransmission will use a different time domain allocation, check
* that we have enough resources */
- NR_pdsch_dmrs_t temp_dmrs = get_dl_dmrs_params(scc,
- dl_bwp,
- &temp_tda,
- layers);
+ NR_pdsch_dmrs_t temp_dmrs = get_dl_dmrs_params(scc, dl_bwp, &temp_tda, layers);
const uint16_t slbitmap = SL_to_bitmap(temp_tda.startSymbolIndex, temp_tda.nrOfSymbols);
- while (rbStart < rbStop && (rballoc_mask[rbStart] & slbitmap) != slbitmap)
+ while (rbStart < rbStop && (rballoc_mask[rbStart] & slbitmap))
rbStart++;
- while (rbStart + rbSize <= rbStop && (rballoc_mask[rbStart + rbSize] & slbitmap) == slbitmap)
+ while (rbStart + rbSize <= rbStop && !(rballoc_mask[rbStart + rbSize] & slbitmap))
rbSize++;
uint32_t new_tbs;
@@ -548,7 +546,9 @@ static bool allocate_dl_retransmission(module_id_t module_id,
/* Find a free CCE */
int CCEIndex = get_cce_index(nr_mac,
- CC_id, slot, UE->rnti,
+ CC_id,
+ slot,
+ UE->rnti,
&sched_ctrl->aggregation_level,
beam,
sched_ctrl->search_space,
@@ -556,10 +556,7 @@ static bool allocate_dl_retransmission(module_id_t module_id,
&sched_ctrl->sched_pdcch,
false);
if (CCEIndex<0) {
- LOG_D(NR_MAC, "[UE %04x][%4d.%2d] could not find free CCE for DL DCI retransmission\n",
- UE->rnti,
- frame,
- slot);
+ LOG_D(NR_MAC, "[UE %04x][%4d.%2d] could not find free CCE for DL DCI retransmission\n", UE->rnti, frame, slot);
return false;
}
@@ -571,30 +568,27 @@ static bool allocate_dl_retransmission(module_id_t module_id,
int r_pucch = nr_get_pucch_resource(sched_ctrl->coreset, ul_bwp->pucch_Config, CCEIndex);
alloc = nr_acknack_scheduling(nr_mac, UE, frame, slot, beam, r_pucch, 0);
if (alloc < 0) {
- LOG_D(NR_MAC, "[UE %04x][%4d.%2d] could not find PUCCH for DL DCI retransmission\n",
- UE->rnti,
- frame,
- slot);
+ LOG_D(NR_MAC, "[UE %04x][%4d.%2d] could not find PUCCH for DL DCI retransmission\n", UE->rnti, frame, slot);
return false;
}
}
sched_ctrl->cce_index = CCEIndex;
fill_pdcch_vrb_map(nr_mac,
- /* CC_id = */ 0,
+ CC_id,
&sched_ctrl->sched_pdcch,
CCEIndex,
sched_ctrl->aggregation_level,
beam);
/* just reuse from previous scheduling opportunity, set new start RB */
sched_ctrl->sched_pdsch = *retInfo;
- sched_ctrl->sched_pdsch.rbStart = rbStart;
+ sched_ctrl->sched_pdsch.rbStart = rbStart - bwp_info.bwpStart;
sched_ctrl->sched_pdsch.pucch_allocation = alloc;
/* retransmissions: directly allocate */
*n_rb_sched -= sched_ctrl->sched_pdsch.rbSize;
- for (int rb = 0; rb < sched_ctrl->sched_pdsch.rbSize; rb++)
- rballoc_mask[rb + sched_ctrl->sched_pdsch.rbStart] ^= SL_to_bitmap(retInfo->tda_info.startSymbolIndex, retInfo->tda_info.nrOfSymbols);
+ for (int rb = rbStart; rb < sched_ctrl->sched_pdsch.rbSize; rb++)
+ rballoc_mask[rb] |= SL_to_bitmap(retInfo->tda_info.startSymbolIndex, retInfo->tda_info.nrOfSymbols);
return true;
}
@@ -614,8 +608,7 @@ static void pf_dl(module_id_t module_id,
sub_frame_t slot,
NR_UE_info_t **UE_list,
int max_num_ue,
- int n_rb_sched,
- uint16_t *rballoc_mask)
+ int n_rb_sched)
{
gNB_MAC_INST *mac = RC.nrmac[module_id];
NR_ServingCellConfigCommon_t *scc=mac->common_channels[0].ServingCellConfigCommon;
@@ -651,7 +644,7 @@ static void pf_dl(module_id_t module_id,
/* retransmission */
if (sched_pdsch->dl_harq_pid >= 0) {
/* Allocate retransmission */
- bool r = allocate_dl_retransmission(module_id, frame, slot, rballoc_mask, &n_rb_sched, UE, sched_pdsch->dl_harq_pid);
+ bool r = allocate_dl_retransmission(module_id, frame, slot, &n_rb_sched, UE, sched_pdsch->dl_harq_pid);
if (!r) {
LOG_D(NR_MAC, "[UE %04x][%4d.%2d] DL retransmission could not be allocated\n",
@@ -742,11 +735,60 @@ static void pf_dl(module_id_t module_id,
NR_sched_pdsch_t *sched_pdsch = &sched_ctrl->sched_pdsch;
sched_pdsch->dl_harq_pid = sched_ctrl->available_dl_harq.head;
+ /* MCS has been set above */
+ sched_pdsch->time_domain_allocation = get_dl_tda(mac, scc, slot);
+ AssertFatal(sched_pdsch->time_domain_allocation>=0,"Unable to find PDSCH time domain allocation in list\n");
+
+ const int coresetid = sched_ctrl->coreset->controlResourceSetId;
+ sched_pdsch->tda_info = get_dl_tda_info(dl_bwp,
+ sched_ctrl->search_space->searchSpaceType->present,
+ sched_pdsch->time_domain_allocation,
+ scc->dmrs_TypeA_Position,
+ 1,
+ TYPE_C_RNTI_,
+ coresetid,
+ false);
+ AssertFatal(sched_pdsch->tda_info.valid_tda, "Invalid TDA from get_dl_tda_info\n");
+
+ NR_tda_info_t *tda_info = &sched_pdsch->tda_info;
+
+ const uint16_t slbitmap = SL_to_bitmap(tda_info->startSymbolIndex, tda_info->nrOfSymbols);
+
+ // TODO assuming beam 0 for now
+ uint16_t *rballoc_mask = mac->common_channels[CC_id].vrb_map[0];
+ dl_bwp_info_t bwp_info = get_bwp_start_size(mac, iterator->UE);
+ int rbStart = 0; // WRT BWP start
+ int rbStop = bwp_info.bwpSize - 1;
+ int bwp_start = bwp_info.bwpStart;
+ // Freq-demain allocation
+ while (rbStart < rbStop && (rballoc_mask[rbStart + bwp_start] & slbitmap))
+ rbStart++;
+
+ uint16_t max_rbSize = 1;
+
+ while (rbStart + max_rbSize <= rbStop && !(rballoc_mask[rbStart + max_rbSize + bwp_start] & slbitmap))
+ max_rbSize++;
+
+ if (max_rbSize < min_rbSize) {
+ LOG_D(NR_MAC,
+ "(%d.%d) Cannot schedule RNTI %04x, rbStart %d, rbSize %d, rbStop %d\n",
+ frame,
+ slot,
+ rnti,
+ rbStart,
+ max_rbSize,
+ rbStop);
+ iterator++;
+ continue;
+ }
+
// TODO properly set the beam index (currently only done for RA)
int beam = 0;
int CCEIndex = get_cce_index(mac,
- CC_id, slot, iterator->UE->rnti,
+ CC_id,
+ slot,
+ iterator->UE->rnti,
&sched_ctrl->aggregation_level,
beam,
sched_ctrl->search_space,
@@ -780,48 +822,9 @@ static void pf_dl(module_id_t module_id,
}
sched_ctrl->cce_index = CCEIndex;
- fill_pdcch_vrb_map(mac,
- /* CC_id = */ 0,
- &sched_ctrl->sched_pdcch,
- CCEIndex,
- sched_ctrl->aggregation_level,
- beam);
+ fill_pdcch_vrb_map(mac, CC_id, &sched_ctrl->sched_pdcch, CCEIndex, sched_ctrl->aggregation_level, beam);
- /* MCS has been set above */
- sched_pdsch->time_domain_allocation = get_dl_tda(mac, scc, slot);
- AssertFatal(sched_pdsch->time_domain_allocation>=0,"Unable to find PDSCH time domain allocation in list\n");
-
- const int coresetid = sched_ctrl->coreset->controlResourceSetId;
- sched_pdsch->tda_info = get_dl_tda_info(dl_bwp,
- sched_ctrl->search_space->searchSpaceType->present,
- sched_pdsch->time_domain_allocation,
- scc->dmrs_TypeA_Position,
- 1,
- TYPE_C_RNTI_,
- coresetid,
- false);
- AssertFatal(sched_pdsch->tda_info.valid_tda, "Invalid TDA from get_dl_tda_info\n");
-
- NR_tda_info_t *tda_info = &sched_pdsch->tda_info;
-
- const uint16_t slbitmap = SL_to_bitmap(tda_info->startSymbolIndex, tda_info->nrOfSymbols);
-
- int rbStop = 0;
- int rbStart = 0;
- get_start_stop_allocation(mac, iterator->UE, &rbStart, &rbStop);
- // Freq-demain allocation
- while (rbStart < rbStop && (rballoc_mask[rbStart] & slbitmap) != slbitmap)
- rbStart++;
-
- uint16_t max_rbSize = 1;
-
- while (rbStart + max_rbSize <= rbStop && (rballoc_mask[rbStart + max_rbSize] & slbitmap) == slbitmap)
- max_rbSize++;
-
- sched_pdsch->dmrs_parms = get_dl_dmrs_params(scc,
- dl_bwp,
- tda_info,
- sched_pdsch->nrOfLayers);
+ sched_pdsch->dmrs_parms = get_dl_dmrs_params(scc, dl_bwp, tda_info, sched_pdsch->nrOfLayers);
sched_pdsch->Qm = nr_get_Qm_dl(sched_pdsch->mcs, dl_bwp->mcsTableIdx);
sched_pdsch->R = nr_get_code_rate_dl(sched_pdsch->mcs, dl_bwp->mcsTableIdx);
sched_pdsch->pucch_allocation = alloc;
@@ -850,8 +853,8 @@ static void pf_dl(module_id_t module_id,
/* transmissions: directly allocate */
n_rb_sched -= sched_pdsch->rbSize;
- for (int rb = 0; rb < sched_pdsch->rbSize; rb++)
- rballoc_mask[rb + sched_pdsch->rbStart] ^= slbitmap;
+ for (int rb = bwp_start; rb < sched_pdsch->rbSize; rb++)
+ rballoc_mask[rb + sched_pdsch->rbStart] |= slbitmap;
remainUEs--;
iterator++;
@@ -866,45 +869,11 @@ static void nr_fr1_dlsch_preprocessor(module_id_t module_id, frame_t frame, sub_
return;
NR_ServingCellConfigCommon_t *scc = RC.nrmac[module_id]->common_channels[0].ServingCellConfigCommon;
- const int CC_id = 0;
- /* Get bwpSize and TDAfrom the first UE */
- /* This is temporary and it assumes all UEs have the same BWP and TDA*/
- NR_UE_info_t *UE=UE_info->list[0];
- NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl;
- NR_UE_DL_BWP_t *current_BWP = &UE->current_DL_BWP;
- const int tda = get_dl_tda(RC.nrmac[module_id], scc, slot);
- int startSymbolIndex, nrOfSymbols;
- const int coresetid = sched_ctrl->coreset->controlResourceSetId;
- const struct NR_PDSCH_TimeDomainResourceAllocationList *tdaList =
- get_dl_tdalist(current_BWP, coresetid, sched_ctrl->search_space->searchSpaceType->present, TYPE_C_RNTI_);
- AssertFatal(tda < tdaList->list.count, "time_domain_allocation %d>=%d\n", tda, tdaList->list.count);
- const int startSymbolAndLength = tdaList->list.array[tda]->startSymbolAndLength;
- SLIV2SL(startSymbolAndLength, &startSymbolIndex, &nrOfSymbols);
-
- const uint16_t bwpSize = current_BWP->BWPSize;
- const uint16_t BWPStart = current_BWP->BWPStart;
-
- const uint16_t slbitmap = SL_to_bitmap(startSymbolIndex, nrOfSymbols);
- // TODO improve handling of beam in vrb_map (for now just using 0)
- uint16_t *vrb_map = RC.nrmac[module_id]->common_channels[CC_id].vrb_map[0];
- uint16_t rballoc_mask[bwpSize];
- int n_rb_sched = 0;
-
- for (int i = 0; i < bwpSize; i++) {
- // calculate mask: init with "NOT" vrb_map:
- // if any RB in vrb_map is blocked (1), the current RBG will be 0
- rballoc_mask[i] = (~vrb_map[i+BWPStart])&0x3fff; //bitwise not and 14 symbols
-
- // if all the pdsch symbols are free
- if ((rballoc_mask[i]&slbitmap) == slbitmap) {
- n_rb_sched++;
- }
- }
+ int bw = scc->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth;
/* Retrieve amount of data to send for this UE */
nr_store_dlsch_buffer(module_id, frame, slot);
- int bw = scc->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth;
int average_agg_level = 4; // TODO find a better estimation
int max_sched_ues = bw / (average_agg_level * NR_NB_REG_PER_CCE);
@@ -917,8 +886,7 @@ static void nr_fr1_dlsch_preprocessor(module_id_t module_id, frame_t frame, sub_
slot,
UE_info->list,
max_sched_ues,
- n_rb_sched,
- rballoc_mask);
+ bw); // we set the whole BW as max number
}
nr_pp_impl_dl nr_init_fr1_dlsch_preprocessor(int CC_id) {
@@ -985,7 +953,7 @@ void nr_schedule_ue_spec(module_id_t module_id,
* Possible improvement: take the periodicity from input file.
* If such UE is not scheduled now, it will be by the preprocessor later.
* If we add the CE, ta_apply will be reset */
- if (frame == (sched_ctrl->ta_frame + 100) % 1024) {
+ if (frame == ((sched_ctrl->ta_frame + 100) % 1024) && !nr_timer_is_active(&sched_ctrl->transm_interrupt)) {
sched_ctrl->ta_apply = true; /* the timer is reset once TA CE is scheduled */
LOG_D(NR_MAC, "[UE %04x][%d.%d] UL timing alignment procedures: setting flag for Timing Advance command\n", UE->rnti, frame, slot);
}
@@ -1093,8 +1061,9 @@ void nr_schedule_ue_spec(module_id_t module_id,
const int pduindex = gNB_mac->pdu_index[CC_id]++;
pdsch_pdu->pduIndex = pduindex;
- pdsch_pdu->BWPSize = current_BWP->BWPSize;
- pdsch_pdu->BWPStart = current_BWP->BWPStart;
+ dl_bwp_info_t bwp_info = get_bwp_start_size(gNB_mac, UE);
+ pdsch_pdu->BWPSize = bwp_info.bwpSize;
+ pdsch_pdu->BWPStart = bwp_info.bwpStart;
pdsch_pdu->SubcarrierSpacing = current_BWP->scs;
pdsch_pdu->CyclicPrefix = current_BWP->cyclicprefix ? *current_BWP->cyclicprefix : 0;
@@ -1208,17 +1177,9 @@ void nr_schedule_ue_spec(module_id_t module_id,
AssertFatal(pdsch_Config == NULL || pdsch_Config->resourceAllocation == NR_PDSCH_Config__resourceAllocation_resourceAllocationType1,
"Only frequency resource allocation type 1 is currently supported\n");
-
- // For a PDSCH scheduled with a DCI format 1_0 in any type of PDCCH common search space, regardless of which
- // bandwidth part is the active bandwidth part, RB numbering starts from the lowest RB of the CORESET in which the
- // DCI was received; otherwise RB numbering starts from the lowest RB in the determined downlink bandwidth part.
- // TS 38.214 Section 5.1.2.2.2
- int rbStop = 0;
- int rbStart = 0;
- get_start_stop_allocation(gNB_mac, UE, &rbStart, &rbStop);
dci_payload.frequency_domain_assignment.val = PRBalloc_to_locationandbandwidth0(pdsch_pdu->rbSize,
- pdsch_pdu->rbStart - rbStart,
- rbStop - rbStart + 1);
+ pdsch_pdu->rbStart,
+ pdsch_pdu->BWPSize);
dci_payload.format_indicator = 1;
dci_payload.time_domain_assignment.val = sched_pdsch->time_domain_allocation;
dci_payload.mcs = sched_pdsch->mcs;
diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c
index 834e42d7a3..5930ef7f44 100644
--- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c
+++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c
@@ -53,6 +53,8 @@
#include "uper_encoder.h"
#include "uper_decoder.h"
+#include "SIMULATION/TOOLS/sim.h" // for taus
+
#define ENABLE_MAC_PAYLOAD_DEBUG
#define DEBUG_gNB_SCHEDULER 1
@@ -2673,6 +2675,16 @@ void mac_remove_nr_ue(gNB_MAC_INST *nr_mac, rnti_t rnti)
memcpy(UE_info->list, newUEs, sizeof(UE_info->list));
NR_SCHED_UNLOCK(&UE_info->mutex);
+ const int CC_id = 0;
+ NR_COMMON_channels_t *cc = &nr_mac->common_channels[CC_id];
+ for (int i = 0; i < NR_NB_RA_PROC_MAX; i++) {
+ NR_RA_t *ra = &cc->ra[i];
+ if (ra->rnti == UE->rnti) {
+ nr_clear_ra_proc(ra);
+ ra->cfra = 0;
+ }
+ }
+
delete_nr_ue_data(UE, nr_mac->common_channels, &UE_info->uid_allocator);
}
@@ -2733,9 +2745,8 @@ void nr_csirs_scheduling(int Mod_idP, frame_t frame, sub_frame_t slot, int n_slo
if (UE_info->sched_csirs & (1 << dl_bwp->bwp_id))
continue;
NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl;
- if (sched_ctrl->rrc_processing_timer > 0) {
+ if (nr_timer_is_active(&sched_ctrl->transm_interrupt))
continue;
- }
if (!UE->sc_info.csi_MeasConfig)
continue;
@@ -2949,28 +2960,41 @@ void nr_csirs_scheduling(int Mod_idP, frame_t frame, sub_frame_t slot, int n_slo
}
}
+static void nr_mac_clean_cellgroup(NR_CellGroupConfig_t *cell_group)
+{
+ /* remove a reconfigurationWithSync, we don't need it anymore */
+ if (cell_group->spCellConfig->reconfigurationWithSync != NULL) {
+ ASN_STRUCT_FREE(asn_DEF_NR_ReconfigurationWithSync, cell_group->spCellConfig->reconfigurationWithSync);
+ cell_group->spCellConfig->reconfigurationWithSync = NULL;
+ }
+ /* remove the rlc_BearerToReleaseList, we don't need it anymore */
+ if (cell_group->rlc_BearerToReleaseList != NULL) {
+ struct NR_CellGroupConfig__rlc_BearerToReleaseList *l = cell_group->rlc_BearerToReleaseList;
+ asn_sequence_del(&l->list, l->list.count, /* free */ 1);
+ free(cell_group->rlc_BearerToReleaseList);
+ cell_group->rlc_BearerToReleaseList = NULL;
+ }
+ /* remove reestablishRLC, we don't need it anymore */
+ for (int i = 0; i < cell_group->rlc_BearerToAddModList->list.count; ++i)
+ free_and_zero(cell_group->rlc_BearerToAddModList->list.array[i]->reestablishRLC);
+}
+
static void nr_mac_apply_cellgroup(gNB_MAC_INST *mac, NR_UE_info_t *UE, frame_t frame, sub_frame_t slot)
{
- LOG_D(NR_MAC, "%4d.%2d RNTI %04x: RRC processing timer expired\n", frame, slot, UE->rnti);
+ LOG_D(NR_MAC, "%4d.%2d RNTI %04x: UE inactivity timer expired\n", frame, slot, UE->rnti);
/* check if there is a new CellGroupConfig to be applied */
- if (UE->apply_cellgroup && UE->reconfigCellGroup != NULL) {
- LOG_D(NR_MAC, "%4d.%2d RNTI %04x: Apply CellGroupConfig after RRC processing timer expiry\n", frame, slot, UE->rnti);
+ if (UE->interrupt_action == FOLLOW_INSYNC_RECONFIG && UE->reconfigCellGroup != NULL) {
+ LOG_D(NR_MAC, "%4d.%2d RNTI %04x: Apply CellGroupConfig after UE inactivity\n", frame, slot, UE->rnti);
ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, UE->CellGroup);
UE->CellGroup = UE->reconfigCellGroup;
UE->reconfigCellGroup = NULL;
- UE->apply_cellgroup = false;
+ UE->interrupt_action = FOLLOW_INSYNC;
if (LOG_DEBUGFLAG(DEBUG_ASN1))
xer_fprint(stdout, &asn_DEF_NR_CellGroupConfig, (const void *)UE->CellGroup);
- /* remove the rlc_BearerToReleaseList, we don't need it anymore */
- if (UE->CellGroup->rlc_BearerToReleaseList != NULL) {
- struct NR_CellGroupConfig__rlc_BearerToReleaseList *l = UE->CellGroup->rlc_BearerToReleaseList;
- asn_sequence_del(&l->list, l->list.count, /* free */1);
- free(UE->CellGroup->rlc_BearerToReleaseList);
- UE->CellGroup->rlc_BearerToReleaseList = NULL;
- }
+ nr_mac_clean_cellgroup(UE->CellGroup);
}
NR_ServingCellConfigCommon_t *scc = mac->common_channels[0].ServingCellConfigCommon;
@@ -2987,31 +3011,42 @@ static void nr_mac_apply_cellgroup(gNB_MAC_INST *mac, NR_UE_info_t *UE, frame_t
}
}
-int nr_mac_enable_ue_rrc_processing_timer(gNB_MAC_INST *mac, NR_UE_info_t *UE, bool apply_cellgroup)
+int nr_mac_get_reconfig_delay_slots(NR_SubcarrierSpacing_t scs)
+{
+ /* we previously assumed a specific "slot ahead" value for the PHY processing
+ * time. However, we cannot always know it (e.g., third-party PHY), so simply
+ * assume a tentative worst-case slot processing time */
+ const uint16_t sl_ahead = 10;
+ /* 16ms seems to be the most common: See 38.331 Tab 12.1-1 */
+ int delay_ms = NR_RRC_RECONFIGURATION_DELAY_MS + NR_RRC_BWP_SWITCHING_DELAY_MS;
+ return (delay_ms << scs) + sl_ahead;
+}
+
+int nr_mac_interrupt_ue_transmission(gNB_MAC_INST *mac, NR_UE_info_t *UE, interrupt_followup_action_t action, int slots)
{
DevAssert(mac != NULL);
DevAssert(UE != NULL);
NR_SCHED_ENSURE_LOCKED(&mac->sched_lock);
- const uint16_t sl_ahead = mac->if_inst->sl_ahead;
- // TODO: account for BWP switch with NR_RRC_BWP_SWITCHING_DELAY_MS
- int delay = NR_RRC_RECONFIGURATION_DELAY_MS;
- NR_SubcarrierSpacing_t scs = UE->current_UL_BWP.scs;
+ nr_timer_setup(&UE->UE_sched_ctrl.transm_interrupt, slots, 1);
+ nr_timer_start(&UE->UE_sched_ctrl.transm_interrupt);
+ UE->interrupt_action = action;
- UE->UE_sched_ctrl.rrc_processing_timer = (delay << scs) + sl_ahead;
- UE->apply_cellgroup = apply_cellgroup;
- AssertFatal(!UE->apply_cellgroup || (UE->apply_cellgroup && UE->reconfigCellGroup),
- "logic bug: apply_cellgroup %d and UE->reconfigCellGroup %p: did you try to apply a cellGroup, while none is deposited?\n",
- UE->apply_cellgroup,
- UE->reconfigCellGroup);
-
- // it might happen that timing advance command should be sent during the RRC
- // processing timer. To prevent this, set a variable as if we would have just
+ // it might happen that timing advance command should be sent during the UE
+ // inactivity time. To prevent this, set a variable as if we would have just
// sent it. This way, another TA command will for sure be sent in some
- // frames, after RRC processing timer.
+ // frames, after the inactivity of the UE.
UE->UE_sched_ctrl.ta_frame = (mac->frame - 1 + 1024) % 1024;
- LOG_D(NR_MAC, "UE %04x: Activate RRC processing timer (%d ms)\n", UE->rnti, delay);
+ LOG_D(NR_MAC, "UE %04x: Interrupt UE transmission (%d slots) action %d reconfigCellGroup %p\n", UE->rnti, slots, UE->interrupt_action, UE->reconfigCellGroup);
+ return 0;
+}
+
+int nr_transmission_action_indicator_stop(gNB_MAC_INST *mac, NR_UE_info_t *UE_info)
+{
+ int delay = nr_mac_get_reconfig_delay_slots(UE_info->current_DL_BWP.scs);
+ nr_mac_interrupt_ue_transmission(mac, UE_info, FOLLOW_OUTOFSYNC, delay);
+ LOG_I(NR_MAC, "gNB-DU received the TransmissionActionIndicator with Stop value for UE %04x\n", UE_info->rnti);
return 0;
}
@@ -3073,9 +3108,12 @@ void nr_mac_update_timers(module_id_t module_id,
/* check if UL failure and trigger release request if necessary */
nr_mac_check_ul_failure(mac, UE->rnti, sched_ctrl);
- if (sched_ctrl->rrc_processing_timer > 0) {
- sched_ctrl->rrc_processing_timer--;
- if (sched_ctrl->rrc_processing_timer == 0)
+ if (nr_timer_tick(&sched_ctrl->transm_interrupt)) {
+ /* expired */
+ nr_timer_stop(&sched_ctrl->transm_interrupt);
+ if (UE->interrupt_action == FOLLOW_OUTOFSYNC)
+ nr_mac_trigger_ul_failure(sched_ctrl, UE->current_DL_BWP.scs);
+ else
nr_mac_apply_cellgroup(mac, UE, frame, slot);
}
}
@@ -3264,7 +3302,27 @@ void nr_mac_reset_ul_failure(NR_UE_sched_ctrl_t *sched_ctrl)
sched_ctrl->pusch_consecutive_dtx_cnt = 0;
}
-void nr_mac_check_ul_failure(const gNB_MAC_INST *nrmac, int rnti, NR_UE_sched_ctrl_t *sched_ctrl)
+/* \brief trigger a release request towards the CU.
+ * \returns true if release was requested, else false (in which case the CU
+ * does not know the UE, and we can safely remove it). */
+bool nr_mac_request_release_ue(const gNB_MAC_INST *nrmac, int rnti)
+{
+ if (!du_exists_f1_ue_data(rnti)) {
+ LOG_W(NR_MAC, "UE %04x: no CU-UE ID stored, cannot request release\n", rnti);
+ return false;
+ }
+ f1_ue_data_t ue_data = du_get_f1_ue_data(rnti);
+ f1ap_ue_context_release_req_t request = {
+ .gNB_CU_ue_id = ue_data.secondary_ue,
+ .gNB_DU_ue_id = rnti,
+ .cause = F1AP_CAUSE_RADIO_NETWORK,
+ .cause_value = F1AP_CauseRadioNetwork_rl_failure_others,
+ };
+ nrmac->mac_rrc.ue_context_release_request(&request);
+ return true;
+}
+
+void nr_mac_check_ul_failure(gNB_MAC_INST *nrmac, int rnti, NR_UE_sched_ctrl_t *sched_ctrl)
{
if (!sched_ctrl->ul_failure)
return;
@@ -3274,14 +3332,9 @@ void nr_mac_check_ul_failure(const gNB_MAC_INST *nrmac, int rnti, NR_UE_sched_ct
* stop at 0 and we wait for a UE release command from upper layers */
if (sched_ctrl->ul_failure_timer == 1) {
LOG_W(MAC, "UE %04x: request release after UL failure timer expiry\n", rnti);
- f1_ue_data_t ue_data = du_get_f1_ue_data(rnti);
- f1ap_ue_context_release_req_t request = {
- .gNB_CU_ue_id = ue_data.secondary_ue,
- .gNB_DU_ue_id = rnti,
- .cause = F1AP_CAUSE_RADIO_NETWORK,
- .cause_value = F1AP_CauseRadioNetwork_rl_failure_others,
- };
- nrmac->mac_rrc.ue_context_release_request(&request);
+ bool requested = nr_mac_request_release_ue(nrmac, rnti);
+ if (!requested)
+ nr_mac_release_ue(nrmac, rnti);
}
}
@@ -3382,3 +3435,26 @@ bool nr_mac_remove_lcid(NR_UE_sched_ctrl_t *sched_ctrl, long lcid)
seq_arr_erase(&sched_ctrl->lc_config, elm.it);
return true;
}
+
+static const NR_RA_t *find_nr_RA_rnti(const NR_RA_t *ra_base, int ra_count, rnti_t rnti)
+{
+ for (int i = 0; i < ra_count; ++i) {
+ const NR_RA_t *ra = &ra_base[i];
+ if (ra->ra_state != nrRA_gNB_IDLE && ra->rnti == rnti)
+ return ra;
+ }
+ return NULL;
+}
+
+bool nr_mac_get_new_rnti(NR_UEs_t *UEs, const NR_RA_t *ra_base, int ra_count, rnti_t *rnti)
+{
+ int loop = 0;
+ bool exist_connected_ue, exist_in_pending_ra_ue;
+ do {
+ *rnti = (taus() % 0xffef) + 1;
+ exist_connected_ue = find_nr_UE(UEs, *rnti) != NULL;
+ exist_in_pending_ra_ue = find_nr_RA_rnti(ra_base, ra_count, *rnti) != NULL;
+ loop++;
+ } while (loop < 100 && (exist_connected_ue || exist_in_pending_ra_ue));
+ return loop < 100; // nothing found: loop count 100
+}
diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_srs.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_srs.c
index 5045d6cbe8..945e078e37 100644
--- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_srs.c
+++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_srs.c
@@ -533,7 +533,7 @@ void nr_schedule_srs(int module_id, frame_t frame, int slot)
sched_ctrl->sched_srs.srs_scheduled = false;
}
- if ((sched_ctrl->ul_failure && !get_softmodem_params()->phy_test) || sched_ctrl->rrc_processing_timer > 0) {
+ if ((sched_ctrl->ul_failure && !get_softmodem_params()->phy_test) || nr_timer_is_active(&sched_ctrl->transm_interrupt)) {
continue;
}
diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c
index facef09b52..ee573e89a1 100644
--- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c
+++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c
@@ -223,7 +223,7 @@ void nr_csi_meas_reporting(int Mod_idP,
NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl;
NR_UE_UL_BWP_t *ul_bwp = &UE->current_UL_BWP;
const int n_slots_frame = nr_slots_per_frame[ul_bwp->scs];
- if ((sched_ctrl->rrc_processing_timer > 0) || (sched_ctrl->ul_failure && !get_softmodem_params()->phy_test)) {
+ if (nr_timer_is_active(&sched_ctrl->transm_interrupt) || (sched_ctrl->ul_failure && !get_softmodem_params()->phy_test)) {
continue;
}
const NR_CSI_MeasConfig_t *csi_measconfig = UE->sc_info.csi_MeasConfig;
@@ -1389,7 +1389,7 @@ void nr_sr_reporting(gNB_MAC_INST *nrmac, frame_t SFN, sub_frame_t slot)
NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl;
NR_UE_UL_BWP_t *ul_bwp = &UE->current_UL_BWP;
const int n_slots_frame = nr_slots_per_frame[ul_bwp->scs];
- if (sched_ctrl->ul_failure || sched_ctrl->rrc_processing_timer > 0)
+ if (sched_ctrl->ul_failure || nr_timer_is_active(&sched_ctrl->transm_interrupt))
continue;
NR_PUCCH_Config_t *pucch_Config = ul_bwp->pucch_Config;
diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c
index 4599349a04..4c0d40d24f 100644
--- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c
+++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c
@@ -501,7 +501,7 @@ static int nr_process_mac_pdu(instance_t module_idP,
break;
default:
- LOG_E(NR_MAC, "RNTI %0x, received unknown MAC header (LCID = 0x%02x)\n", UE->rnti, rx_lcid);
+ LOG_E(NR_MAC, "RNTI %0x [%d.%d], received unknown MAC header (LCID = 0x%02x)\n", UE->rnti, frameP, slot, rx_lcid);
return -1;
break;
}
@@ -561,12 +561,16 @@ static void abort_nr_ul_harq(NR_UE_info_t *UE, int8_t harq_pid)
sched_ctrl->sched_ul_bytes = 0;
}
-static bool get_UE_waiting_CFRA_msg3(const gNB_MAC_INST *gNB_mac, const int CC_id, const frame_t frame, const sub_frame_t slot)
+static bool get_UE_waiting_CFRA_msg3(const gNB_MAC_INST *gNB_mac,
+ const int CC_id,
+ const frame_t frame,
+ const sub_frame_t slot,
+ rnti_t rnti)
{
bool UE_waiting_CFRA_msg3 = false;
for (int i = 0; i < NR_NB_RA_PROC_MAX; i++) {
const NR_RA_t *ra = &gNB_mac->common_channels[CC_id].ra[i];
- if (ra->cfra == true && ra->ra_state == nrRA_WAIT_Msg3 && frame == ra->Msg3_frame && slot == ra->Msg3_slot) {
+ if (ra->cfra && ra->ra_state == nrRA_WAIT_Msg3 && frame == ra->Msg3_frame && slot == ra->Msg3_slot && rnti == ra->rnti) {
UE_waiting_CFRA_msg3 = true;
break;
}
@@ -584,7 +588,7 @@ void handle_nr_ul_harq(const int CC_idP,
NR_SCHED_LOCK(&nrmac->sched_lock);
NR_UE_info_t *UE = find_nr_UE(&nrmac->UE_info, crc_pdu->rnti);
- bool UE_waiting_CFRA_msg3 = get_UE_waiting_CFRA_msg3(nrmac, CC_idP, frame, slot);
+ bool UE_waiting_CFRA_msg3 = get_UE_waiting_CFRA_msg3(nrmac, CC_idP, frame, slot, crc_pdu->rnti);
if (!UE || UE_waiting_CFRA_msg3 == true) {
LOG_D(NR_MAC, "handle harq for rnti %04x, in RA process\n", crc_pdu->rnti);
@@ -700,7 +704,7 @@ static void _nr_rx_sdu(const module_id_t gnb_mod_idP,
const int pusch_failure_thres = gNB_mac->pusch_failure_thres;
NR_UE_info_t *UE = find_nr_UE(&gNB_mac->UE_info, current_rnti);
- bool UE_waiting_CFRA_msg3 = get_UE_waiting_CFRA_msg3(gNB_mac, CC_idP, frameP, slotP);
+ bool UE_waiting_CFRA_msg3 = get_UE_waiting_CFRA_msg3(gNB_mac, CC_idP, frameP, slotP, current_rnti);
if (UE && UE_waiting_CFRA_msg3 == false) {
@@ -1640,7 +1644,8 @@ static bool allocate_ul_retransmission(gNB_MAC_INST *nrmac,
NR_UE_UL_BWP_t *ul_bwp = &UE->current_UL_BWP;
int rbStart = 0; // wrt BWP start
- const uint16_t bwpSize = ul_bwp->BWPSize;
+ const uint32_t bwpSize = ul_bwp->BWPSize;
+ const uint32_t bwpStart = ul_bwp->BWPStart;
const uint8_t nrOfLayers = retInfo->nrOfLayers;
LOG_D(NR_MAC,"retInfo->time_domain_allocation = %d, tda = %d\n", retInfo->time_domain_allocation, tda);
LOG_D(NR_MAC,"tbs %d\n",retInfo->tb_size);
@@ -1651,11 +1656,12 @@ static bool allocate_ul_retransmission(gNB_MAC_INST *nrmac,
tda);
if (!tda_info.valid_tda)
return false;
+
bool reuse_old_tda = (retInfo->tda_info.startSymbolIndex == tda_info.startSymbolIndex) && (retInfo->tda_info.nrOfSymbols <= tda_info.nrOfSymbols);
if (reuse_old_tda && nrOfLayers == retInfo->nrOfLayers) {
/* Check the resource is enough for retransmission */
const uint16_t slbitmap = SL_to_bitmap(retInfo->tda_info.startSymbolIndex, retInfo->tda_info.nrOfSymbols);
- while (rbStart < bwpSize && (rballoc_mask[rbStart] & slbitmap) != slbitmap)
+ while (rbStart < bwpSize && (rballoc_mask[rbStart + bwpStart] & slbitmap))
rbStart++;
if (rbStart + retInfo->rbSize > bwpSize) {
LOG_D(NR_MAC, "[UE %04x][%4d.%2d] could not allocate UL retransmission: no resources (rbStart %d, retInfo->rbSize %d, bwpSize %d) \n",
@@ -1667,19 +1673,16 @@ static bool allocate_ul_retransmission(gNB_MAC_INST *nrmac,
bwpSize);
return false;
}
- LOG_D(NR_MAC, "%s(): retransmission keeping TDA %d and TBS %d\n", __func__, tda, retInfo->tb_size);
+ LOG_D(NR_MAC, "Retransmission keeping TDA %d and TBS %d\n", tda, retInfo->tb_size);
} else {
- NR_pusch_dmrs_t dmrs_info = get_ul_dmrs_params(scc,
- ul_bwp,
- &tda_info,
- nrOfLayers);
+ NR_pusch_dmrs_t dmrs_info = get_ul_dmrs_params(scc, ul_bwp, &tda_info, nrOfLayers);
/* the retransmission will use a different time domain allocation, check
* that we have enough resources */
const uint16_t slbitmap = SL_to_bitmap(tda_info.startSymbolIndex, tda_info.nrOfSymbols);
- while (rbStart < bwpSize && (rballoc_mask[rbStart] & slbitmap) != slbitmap)
+ while (rbStart < bwpSize && (rballoc_mask[rbStart + bwpStart] & slbitmap))
rbStart++;
int rbSize = 0;
- while (rbStart + rbSize < bwpSize && (rballoc_mask[rbStart + rbSize] & slbitmap) == slbitmap)
+ while (rbStart + rbSize < bwpSize && !(rballoc_mask[rbStart + bwpStart + rbSize] & slbitmap))
rbSize++;
uint32_t new_tbs;
uint16_t new_rbSize;
@@ -1703,7 +1706,7 @@ static bool allocate_ul_retransmission(gNB_MAC_INST *nrmac,
retInfo->tb_size);
return false; /* the maximum TBsize we might have is smaller than what we need */
}
- LOG_D(NR_MAC, "%s(): retransmission with TDA %d->%d and TBS %d -> %d\n", __func__, retInfo->time_domain_allocation, tda, retInfo->tb_size, new_tbs);
+ LOG_D(NR_MAC, "Retransmission with TDA %d->%d and TBS %d -> %d\n", retInfo->time_domain_allocation, tda, retInfo->tb_size, new_tbs);
/* we can allocate it. Overwrite the time_domain_allocation, the number
* of RBs, and the new TB size. The rest is done below */
retInfo->tb_size = new_tbs;
@@ -1715,7 +1718,9 @@ static bool allocate_ul_retransmission(gNB_MAC_INST *nrmac,
/* Find a free CCE */
int CCEIndex = get_cce_index(nrmac,
- CC_id, slot, UE->rnti,
+ CC_id,
+ slot,
+ UE->rnti,
&sched_ctrl->aggregation_level,
0, // TODO use beam index
sched_ctrl->search_space,
@@ -1723,10 +1728,7 @@ static bool allocate_ul_retransmission(gNB_MAC_INST *nrmac,
&sched_ctrl->sched_pdcch,
false);
if (CCEIndex<0) {
- LOG_D(NR_MAC, "[UE %04x][%4d.%2d] no free CCE for retransmission UL DCI UE\n",
- UE->rnti,
- frame,
- slot);
+ LOG_D(NR_MAC, "[UE %04x][%4d.%2d] no free CCE for retransmission UL DCI UE\n", UE->rnti, frame, slot);
return false;
}
@@ -1738,11 +1740,8 @@ static bool allocate_ul_retransmission(gNB_MAC_INST *nrmac,
sched_ctrl->aggregation_level,
0); // TODO use beam index when implemented
- /* frame/slot in sched_pusch has been set previously. In the following, we
- * overwrite the information in the retransmission information before storing
- * as the new scheduling instruction */
- retInfo->frame = sched_ctrl->sched_pusch.frame;
- retInfo->slot = sched_ctrl->sched_pusch.slot;
+ retInfo->frame = (frame + (slot + tda_info.k2) / nr_slots_per_frame[ul_bwp->scs]) % MAX_FRAME_NUMBER;
+ retInfo->slot = (slot + tda_info.k2) % nr_slots_per_frame[ul_bwp->scs];
/* Get previous PSUCH field info */
sched_ctrl->sched_pusch = *retInfo;
NR_sched_pusch_t *sched_pusch = &sched_ctrl->sched_pusch;
@@ -1761,8 +1760,8 @@ static bool allocate_ul_retransmission(gNB_MAC_INST *nrmac,
/* Mark the corresponding RBs as used */
n_rb_sched -= sched_pusch->rbSize;
- for (int rb = 0; rb < sched_ctrl->sched_pusch.rbSize; rb++)
- rballoc_mask[rb + sched_ctrl->sched_pusch.rbStart] ^= SL_to_bitmap(sched_pusch->tda_info.startSymbolIndex, sched_pusch->tda_info.nrOfSymbols);
+ for (int rb = bwpStart; rb < sched_ctrl->sched_pusch.rbSize; rb++)
+ rballoc_mask[rb + sched_ctrl->sched_pusch.rbStart] |= SL_to_bitmap(sched_pusch->tda_info.startSymbolIndex, sched_pusch->tda_info.nrOfSymbols);
return true;
}
@@ -1778,22 +1777,22 @@ static int comparator(const void *p, const void *q) {
static void pf_ul(module_id_t module_id,
frame_t frame,
- sub_frame_t slot,
+ int slot,
+ frame_t sched_frame,
+ int sched_slot,
NR_UE_info_t *UE_list[],
int max_num_ue,
- int n_rb_sched,
- uint16_t *rballoc_mask)
+ int n_rb_sched)
{
-
const int CC_id = 0;
gNB_MAC_INST *nrmac = RC.nrmac[module_id];
NR_ServingCellConfigCommon_t *scc = nrmac->common_channels[CC_id].ServingCellConfigCommon;
-
+
const int min_rb = nrmac->min_grant_prb;
// UEs that could be scheduled
UEsched_t UE_sched[MAX_MOBILES_PER_GNB] = {0};
int remainUEs = max_num_ue;
- int curUE=0;
+ int curUE = 0;
/* Loop UE_list to calculate throughput and coeff */
UE_iterator(UE_list, UE) {
@@ -1805,9 +1804,6 @@ static void pf_ul(module_id_t module_id,
LOG_D(NR_MAC,"pf_ul: preparing UL scheduling for UE %04x\n",UE->rnti);
NR_UE_UL_BWP_t *current_BWP = &UE->current_UL_BWP;
- int rbStart = 0; // wrt BWP start
-
- const uint16_t bwpSize = current_BWP->BWPSize;
NR_sched_pusch_t *sched_pusch = &sched_ctrl->sched_pusch;
const NR_mac_dir_stats_t *stats = &UE->mac_stats.ul;
@@ -1819,12 +1815,16 @@ static void pf_ul(module_id_t module_id,
if(remainUEs == 0)
continue;
+ const int index = ul_buffer_index(sched_frame, sched_slot, current_BWP->scs, nrmac->vrb_map_UL_size);
+ // TODO improve handling of beam in vrb_map (for now just using 0)
+ uint16_t *rballoc_mask = &nrmac->common_channels[CC_id].vrb_map_UL[0][index * MAX_BWP_SIZE];
+
/* Check if retransmission is necessary */
sched_pusch->ul_harq_pid = sched_ctrl->retrans_ul_harq.head;
LOG_D(NR_MAC,"pf_ul: UE %04x harq_pid %d\n",UE->rnti,sched_pusch->ul_harq_pid);
if (sched_pusch->ul_harq_pid >= 0) {
/* Allocate retransmission*/
- const int tda = get_ul_tda(nrmac, scc, sched_pusch->frame, sched_pusch->slot);
+ const int tda = get_ul_tda(nrmac, scc, sched_frame, sched_slot);
bool r = allocate_ul_retransmission(nrmac, frame, slot, rballoc_mask, &n_rb_sched, UE, sched_pusch->ul_harq_pid, scc, tda);
if (!r) {
LOG_D(NR_MAC, "[UE %04x][%4d.%2d] UL retransmission could not be allocated\n",
@@ -1853,10 +1853,10 @@ static void pf_ul(module_id_t module_id,
const int B = max(0, sched_ctrl->estimated_ul_buffer - sched_ctrl->sched_ul_bytes);
/* preprocessor computed sched_frame/sched_slot */
- const bool do_sched = nr_UE_is_to_be_scheduled(scc, 0, UE, sched_pusch->frame, sched_pusch->slot, nrmac->ulsch_max_frame_inactivity);
+ const bool do_sched = nr_UE_is_to_be_scheduled(scc, 0, UE, sched_frame, sched_slot, nrmac->ulsch_max_frame_inactivity);
LOG_D(NR_MAC,"pf_ul: do_sched UE %04x => %s\n",UE->rnti,do_sched ? "yes" : "no");
- if ((B == 0 && !do_sched) || (sched_ctrl->rrc_processing_timer > 0)) {
+ if ((B == 0 && !do_sched) || nr_timer_is_active(&sched_ctrl->transm_interrupt)) {
continue;
}
@@ -1892,7 +1892,7 @@ static void pf_ul(module_id_t module_id,
}
sched_pusch->nrOfLayers = sched_ctrl->srs_feedback.ul_ri + 1;
- sched_pusch->time_domain_allocation = get_ul_tda(nrmac, scc, sched_pusch->frame, sched_pusch->slot);
+ sched_pusch->time_domain_allocation = get_ul_tda(nrmac, scc, sched_frame, sched_slot);
sched_pusch->tda_info = get_ul_tda_info(current_BWP,
sched_ctrl->coreset->controlResourceSetId,
sched_ctrl->search_space->searchSpaceType->present,
@@ -1904,13 +1904,20 @@ static void pf_ul(module_id_t module_id,
&sched_pusch->tda_info,
sched_pusch->nrOfLayers);
- LOG_D(NR_MAC,"Looking for min_rb %d RBs, starting at %d num_dmrs_cdm_grps_no_data %d\n",
- min_rb, rbStart, sched_pusch->dmrs_info.num_dmrs_cdm_grps_no_data);
+ int rbStart = 0; // wrt BWP start
+ LOG_D(NR_MAC,
+ "Looking for min_rb %d RBs, starting at %d num_dmrs_cdm_grps_no_data %d\n",
+ min_rb,
+ rbStart,
+ sched_pusch->dmrs_info.num_dmrs_cdm_grps_no_data);
+ const uint32_t bwpSize = current_BWP->BWPSize;
+ const uint32_t bwpStart = current_BWP->BWPStart;
const uint16_t slbitmap = SL_to_bitmap(sched_pusch->tda_info.startSymbolIndex, sched_pusch->tda_info.nrOfSymbols);
- while (rbStart < bwpSize && (rballoc_mask[rbStart] & slbitmap) != slbitmap)
+ while (rbStart < bwpSize && (rballoc_mask[rbStart + bwpStart] & slbitmap))
rbStart++;
if (rbStart + min_rb >= bwpSize) {
- LOG_D(NR_MAC, "[UE %04x][%4d.%2d] could not allocate continuous UL data: no resources (rbStart %d, min_rb %d, bwpSize %d)\n",
+ LOG_D(NR_MAC,
+ "[UE %04x][%4d.%2d] could not allocate continuous UL data: no resources (rbStart %d, min_rb %d, bwpSize %d)\n",
UE->rnti,
frame,
slot,
@@ -1921,18 +1928,15 @@ static void pf_ul(module_id_t module_id,
}
sched_ctrl->cce_index = CCEIndex;
- fill_pdcch_vrb_map(nrmac,
- CC_id,
- &sched_ctrl->sched_pdcch,
- CCEIndex,
- sched_ctrl->aggregation_level,
- 0); // TODO use beam index);
+ fill_pdcch_vrb_map(nrmac, CC_id, &sched_ctrl->sched_pdcch, CCEIndex, sched_ctrl->aggregation_level, 0); // TODO use beam index
NR_sched_pusch_t *sched_pusch = &sched_ctrl->sched_pusch;
sched_pusch->mcs = min(nrmac->min_grant_mcs, sched_pusch->mcs);
update_ul_ue_R_Qm(sched_pusch->mcs, current_BWP->mcs_table, current_BWP->pusch_Config, &sched_pusch->R, &sched_pusch->Qm);
sched_pusch->rbStart = rbStart;
sched_pusch->rbSize = min_rb;
+ sched_pusch->frame = sched_frame;
+ sched_pusch->slot = sched_slot;
sched_pusch->tb_size = nr_compute_tbs(sched_pusch->Qm,
sched_pusch->R,
sched_pusch->rbSize,
@@ -1963,8 +1967,8 @@ static void pf_ul(module_id_t module_id,
/* Mark the corresponding RBs as used */
n_rb_sched -= sched_pusch->rbSize;
- for (int rb = 0; rb < sched_ctrl->sched_pusch.rbSize; rb++)
- rballoc_mask[rb + sched_ctrl->sched_pusch.rbStart] ^= slbitmap;
+ for (int rb = bwpStart; rb < sched_ctrl->sched_pusch.rbSize; rb++)
+ rballoc_mask[rb + sched_ctrl->sched_pusch.rbStart] |= slbitmap;
remainUEs--;
continue;
@@ -1982,8 +1986,8 @@ static void pf_ul(module_id_t module_id,
UE->ul_thr_ue,
tbs,
coeff_ue);
- UE_sched[curUE].coef=coeff_ue;
- UE_sched[curUE].UE=UE;
+ UE_sched[curUE].coef = coeff_ue;
+ UE_sched[curUE].UE = UE;
curUE++;
}
@@ -2018,7 +2022,7 @@ static void pf_ul(module_id_t module_id,
NR_sched_pusch_t *sched_pusch = &sched_ctrl->sched_pusch;
sched_pusch->nrOfLayers = sched_ctrl->srs_feedback.ul_ri + 1;
- sched_pusch->time_domain_allocation = get_ul_tda(nrmac, scc, sched_pusch->frame, sched_pusch->slot);
+ sched_pusch->time_domain_allocation = get_ul_tda(nrmac, scc, sched_frame, sched_slot);
sched_pusch->tda_info = get_ul_tda_info(current_BWP,
sched_ctrl->coreset->controlResourceSetId,
sched_ctrl->search_space->searchSpaceType->present,
@@ -2030,14 +2034,19 @@ static void pf_ul(module_id_t module_id,
&sched_pusch->tda_info,
sched_pusch->nrOfLayers);
+ const int index = ul_buffer_index(sched_frame, sched_slot, current_BWP->scs, nrmac->vrb_map_UL_size);
+ // TODO improve handling of beam in vrb_map (for now just using 0)
+ uint16_t *rballoc_mask = &nrmac->common_channels[CC_id].vrb_map_UL[0][index * MAX_BWP_SIZE];
+
int rbStart = 0;
const uint16_t slbitmap = SL_to_bitmap(sched_pusch->tda_info.startSymbolIndex, sched_pusch->tda_info.nrOfSymbols);
- const uint16_t bwpSize = current_BWP->BWPSize;
- while (rbStart < bwpSize && (rballoc_mask[rbStart] & slbitmap) != slbitmap)
+ const uint32_t bwpSize = current_BWP->BWPSize;
+ const uint32_t bwpStart = current_BWP->BWPStart;
+ while (rbStart < bwpSize && (rballoc_mask[rbStart + bwpStart] & slbitmap))
rbStart++;
sched_pusch->rbStart = rbStart;
uint16_t max_rbSize = 1;
- while (rbStart + max_rbSize < bwpSize && (rballoc_mask[rbStart + max_rbSize] & slbitmap) == slbitmap)
+ while (rbStart + max_rbSize < bwpSize && !(rballoc_mask[rbStart + bwpStart + max_rbSize] & slbitmap))
max_rbSize++;
if (rbStart + min_rb >= bwpSize || max_rbSize < min_rb) {
@@ -2051,13 +2060,18 @@ static void pf_ul(module_id_t module_id,
iterator++;
continue;
} else
- LOG_D(NR_MAC, "allocating UL data for RNTI %04x (rbStart %d, min_rb %d, max_rbSize %d, bwpSize %d)\n", iterator->UE->rnti, rbStart, min_rb, max_rbSize, bwpSize);
+ LOG_D(NR_MAC,
+ "allocating UL data for RNTI %04x (rbStart %d, min_rb %d, max_rbSize %d, bwpSize %d)\n",
+ iterator->UE->rnti,
+ rbStart,
+ min_rb,
+ max_rbSize,
+ bwpSize);
/* Calculate the current scheduling bytes */
const int B = cmax(sched_ctrl->estimated_ul_buffer - sched_ctrl->sched_ul_bytes, 0);
/* adjust rbSize and MCS according to PHR and BPRE */
- if(sched_ctrl->pcmax!=0 ||
- sched_ctrl->ph!=0) // verify if the PHR related parameter have been initialized
+ if(sched_ctrl->pcmax != 0 || sched_ctrl->ph != 0) // verify if the PHR related parameter have been initialized
nr_ue_max_mcs_min_rb(current_BWP->scs, sched_ctrl->ph, sched_pusch, current_BWP, min_rb, B, &max_rbSize, &sched_pusch->mcs);
if (sched_pusch->mcs < sched_ctrl->ul_bler_stats.mcs)
@@ -2093,23 +2107,28 @@ static void pf_ul(module_id_t module_id,
sched_pusch->rbSize = rbSize;
sched_pusch->tb_size = TBS;
- LOG_D(NR_MAC,"rbSize %d (max_rbSize %d), TBS %d, est buf %d, sched_ul %d, B %d, CCE %d, num_dmrs_symb %d, N_PRB_DMRS %d\n",
- rbSize, max_rbSize,sched_pusch->tb_size, sched_ctrl->estimated_ul_buffer, sched_ctrl->sched_ul_bytes, B,
- sched_ctrl->cce_index,sched_pusch->dmrs_info.num_dmrs_symb,sched_pusch->dmrs_info.N_PRB_DMRS);
+ sched_pusch->frame = sched_frame;
+ sched_pusch->slot = sched_slot;
+ LOG_D(NR_MAC,
+ "rbSize %d (max_rbSize %d), TBS %d, est buf %d, sched_ul %d, B %d, CCE %d, num_dmrs_symb %d, N_PRB_DMRS %d\n",
+ rbSize,
+ max_rbSize,
+ sched_pusch->tb_size,
+ sched_ctrl->estimated_ul_buffer,
+ sched_ctrl->sched_ul_bytes,
+ B,
+ sched_ctrl->cce_index,
+ sched_pusch->dmrs_info.num_dmrs_symb,
+ sched_pusch->dmrs_info.N_PRB_DMRS);
/* Mark the corresponding RBs as used */
sched_ctrl->cce_index = CCEIndex;
- fill_pdcch_vrb_map(nrmac,
- CC_id,
- &sched_ctrl->sched_pdcch,
- CCEIndex,
- sched_ctrl->aggregation_level,
- 0); // TODO use beam index);
+ fill_pdcch_vrb_map(nrmac, CC_id, &sched_ctrl->sched_pdcch, CCEIndex, sched_ctrl->aggregation_level, 0); // TODO use beam index
n_rb_sched -= sched_pusch->rbSize;
- for (int rb = 0; rb < sched_ctrl->sched_pusch.rbSize; rb++)
- rballoc_mask[rb + sched_ctrl->sched_pusch.rbStart] ^= slbitmap;
+ for (int rb = bwpStart; rb < sched_ctrl->sched_pusch.rbSize; rb++)
+ rballoc_mask[rb + sched_ctrl->sched_pusch.rbStart] |= slbitmap;
/* reduce max_num_ue once we are sure UE can be allocated, i.e., has CCE */
remainUEs--;
@@ -2120,92 +2139,25 @@ static void pf_ul(module_id_t module_id,
static bool nr_fr1_ulsch_preprocessor(module_id_t module_id, frame_t frame, sub_frame_t slot)
{
gNB_MAC_INST *nr_mac = RC.nrmac[module_id];
- NR_COMMON_channels_t *cc = nr_mac->common_channels;
- NR_ServingCellConfigCommon_t *scc = cc->ServingCellConfigCommon;
- const NR_SIB1_t *sib1 = nr_mac->common_channels[0].sib1 ? nr_mac->common_channels[0].sib1->message.choice.c1->choice.systemInformationBlockType1 : NULL;
- NR_ServingCellConfigCommonSIB_t *scc_sib1 = sib1 ? sib1->servingCellConfigCommon : NULL;
-
- AssertFatal(scc || scc_sib1, "We need one serving cell config common\n");
-
// no UEs
if (nr_mac->UE_info.list[0] == NULL)
return false;
- const int CC_id = 0;
-
- /* Get the K2 for first UE to compute offset. The other UEs are guaranteed to
- * have the same K2 (we don't support multiple/different K2s via different
- * TDAs yet). If the TDA is negative, it means that there is no UL slot to
- * schedule now (slot + k2 is not UL slot) */
- NR_UE_sched_ctrl_t *sched_ctrl = &nr_mac->UE_info.list[0]->UE_sched_ctrl;
- NR_UE_UL_BWP_t *current_BWP = &nr_mac->UE_info.list[0]->current_UL_BWP;
- int mu = current_BWP->scs;
- const int temp_tda = get_ul_tda(nr_mac, scc, frame, slot);
- NR_PUSCH_TimeDomainResourceAllocationList_t *tdaList = get_ul_tdalist(current_BWP,
- sched_ctrl->coreset->controlResourceSetId,
- sched_ctrl->search_space->searchSpaceType->present,
- TYPE_C_RNTI_);
- int K2 = get_K2(tdaList, temp_tda, mu, scc);
- const int sched_frame = (frame + (slot + K2) / nr_slots_per_frame[mu]) % MAX_FRAME_NUMBER;
- const int sched_slot = (slot + K2) % nr_slots_per_frame[mu];
- const int tda = get_ul_tda(nr_mac, scc, sched_frame, sched_slot);
- if (tda < 0)
- return false;
- DevAssert(K2 == get_K2(tdaList, tda, mu, scc));
-
+ NR_COMMON_channels_t *cc = nr_mac->common_channels;
+ NR_ServingCellConfigCommon_t *scc = cc->ServingCellConfigCommon;
+ const NR_SIB1_t *sib1 = nr_mac->common_channels[0].sib1 ? nr_mac->common_channels[0].sib1->message.choice.c1->choice.systemInformationBlockType1 : NULL;
+ NR_ServingCellConfigCommonSIB_t *scc_sib1 = sib1 ? sib1->servingCellConfigCommon : NULL;
+ AssertFatal(scc || scc_sib1, "We need one serving cell config common\n");
+ const int slots_frame = nr_slots_per_frame[*scc->ssbSubcarrierSpacing];
+ // TODO we assume the same K2 for all UEs
+ const int K2 = nr_mac->radio_config.minRXTXTIME + get_NTN_Koffset(scc);
+ const int sched_frame = (frame + (slot + K2) / slots_frame) % MAX_FRAME_NUMBER;
+ const int sched_slot = (slot + K2) % slots_frame;
if (!is_xlsch_in_slot(nr_mac->ulsch_slot_bitmap[sched_slot / 64], sched_slot))
return false;
- sched_ctrl->sched_pusch.slot = sched_slot;
- sched_ctrl->sched_pusch.frame = sched_frame;
- UE_iterator(nr_mac->UE_info.list, UE2) {
- NR_UE_sched_ctrl_t *sched_ctrl = &UE2->UE_sched_ctrl;
- AssertFatal(K2 == get_K2(tdaList, tda, mu, scc),
- "Different K2, %d(UE%d) != %ld(UE%04x)\n",
- K2, 0, get_K2(tdaList, tda, mu, scc), UE2->rnti);
- sched_ctrl->sched_pusch.slot = sched_slot;
- sched_ctrl->sched_pusch.frame = sched_frame;
- }
-
- /* Change vrb_map_UL to rballoc_mask: check which symbols per RB (in
- * vrb_map_UL) overlap with the "default" tda and exclude those RBs.
- * Calculate largest contiguous RBs */
- const int index = ul_buffer_index(sched_frame, sched_slot, mu, nr_mac->vrb_map_UL_size);
- // TODO improve handling of beam in vrb_map (for now just using 0)
- uint16_t *vrb_map_UL = &nr_mac->common_channels[CC_id].vrb_map_UL[0][index * MAX_BWP_SIZE];
-
- const uint16_t bwpSize = current_BWP->BWPSize;
- const uint16_t bwpStart = current_BWP->BWPStart;
-
- const int startSymbolAndLength = tdaList->list.array[tda]->startSymbolAndLength;
- int startSymbolIndex, nrOfSymbols;
- SLIV2SL(startSymbolAndLength, &startSymbolIndex, &nrOfSymbols);
- const uint16_t symb = SL_to_bitmap(startSymbolIndex, nrOfSymbols);
-
- int st = 0, e = 0, len = 0;
-
- for (int i = 0; i < bwpSize; i++) {
- while ((vrb_map_UL[bwpStart + i] & symb) != 0 && i < bwpSize)
- i++;
- st = i;
- while ((vrb_map_UL[bwpStart + i] & symb) == 0 && i < bwpSize)
- i++;
- if (i - st > len) {
- len = i - st;
- e = i - 1;
- }
- }
- st = e - len + 1;
-
- LOG_D(NR_MAC,"UL %d.%d : start_prb %d, end PRB %d\n",frame,slot,st,e);
-
- uint16_t rballoc_mask[bwpSize];
-
- /* Calculate mask: if any RB in vrb_map_UL is blocked (1), the current RB will be 0 */
- for (int i = 0; i < bwpSize; i++)
- rballoc_mask[i] = (i >= st && i <= e)*SL_to_bitmap(startSymbolIndex, nrOfSymbols);
-
int bw = scc->uplinkConfigCommon->frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth;
+
int average_agg_level = 4; // TODO find a better estimation
int max_sched_ues = bw / (average_agg_level * NR_NB_REG_PER_CCE);
@@ -2213,13 +2165,7 @@ static bool nr_fr1_ulsch_preprocessor(module_id_t module_id, frame_t frame, sub_
max_sched_ues = min(max_sched_ues, MAX_DCI_CORESET);
/* proportional fair scheduling algorithm */
- pf_ul(module_id,
- frame,
- slot,
- nr_mac->UE_info.list,
- max_sched_ues,
- len,
- rballoc_mask);
+ pf_ul(module_id, frame, slot, sched_frame, sched_slot, nr_mac->UE_info.list, max_sched_ues, bw);
return true;
}
diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h
index da3b612561..c5df1782fd 100644
--- a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h
+++ b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h
@@ -53,8 +53,11 @@ void nr_mac_configure_sib19(gNB_MAC_INST *nrmac);
bool nr_mac_add_test_ue(gNB_MAC_INST *nrmac, uint32_t rnti, NR_CellGroupConfig_t *CellGroup);
bool nr_mac_prepare_ra_ue(gNB_MAC_INST *nrmac, uint32_t rnti, NR_CellGroupConfig_t *CellGroup);
+int nr_mac_get_reconfig_delay_slots(NR_SubcarrierSpacing_t scs);
+
bool nr_mac_prepare_cellgroup_update(gNB_MAC_INST *nrmac, NR_UE_info_t *UE, NR_CellGroupConfig_t *CellGroup);
-int nr_mac_enable_ue_rrc_processing_timer(gNB_MAC_INST *mac, NR_UE_info_t *UE, bool apply_cellGroup);
+int nr_mac_interrupt_ue_transmission(gNB_MAC_INST *mac, NR_UE_info_t *UE, interrupt_followup_action_t action, int slots);
+int nr_transmission_action_indicator_stop(gNB_MAC_INST *mac, NR_UE_info_t *UE_info);
void clear_nr_nfapi_information(gNB_MAC_INST *gNB,
int CC_idP,
@@ -461,10 +464,11 @@ void nr_mac_trigger_release_timer(NR_UE_sched_ctrl_t *sched_ctrl, NR_SubcarrierS
bool nr_mac_check_release(NR_UE_sched_ctrl_t *sched_ctrl, int rnti);
void nr_mac_trigger_release_complete(gNB_MAC_INST *mac, int rnti);
void nr_mac_release_ue(gNB_MAC_INST *mac, int rnti);
+bool nr_mac_request_release_ue(const gNB_MAC_INST *nrmac, int rnti);
void nr_mac_trigger_ul_failure(NR_UE_sched_ctrl_t *sched_ctrl, NR_SubcarrierSpacing_t subcarrier_spacing);
void nr_mac_reset_ul_failure(NR_UE_sched_ctrl_t *sched_ctrl);
-void nr_mac_check_ul_failure(const gNB_MAC_INST *nrmac, int rnti, NR_UE_sched_ctrl_t *sched_ctrl);
+void nr_mac_check_ul_failure(gNB_MAC_INST *nrmac, int rnti, NR_UE_sched_ctrl_t *sched_ctrl);
void nr_mac_trigger_reconfiguration(const gNB_MAC_INST *nrmac, const NR_UE_info_t *UE);
@@ -474,4 +478,6 @@ void process_addmod_bearers_cellGroupConfig(NR_UE_sched_ctrl_t *sched_ctrl,
bool nr_mac_add_lcid(NR_UE_sched_ctrl_t *sched_ctrl, const nr_lc_config_t *c);
bool nr_mac_remove_lcid(NR_UE_sched_ctrl_t *sched_ctrl, long lcid);
+bool nr_mac_get_new_rnti(NR_UEs_t *UEs, const NR_RA_t *ra_base, int ra_count, rnti_t *rnti);
+
#endif /*__LAYER2_NR_MAC_PROTO_H__*/
diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c b/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c
index 407e5869ce..63e7ff9d99 100644
--- a/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c
+++ b/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c
@@ -26,6 +26,7 @@
#include "openair2/F1AP/f1ap_common.h"
#include "openair2/LAYER2/nr_rlc/nr_rlc_oai_api.h"
#include "F1AP_CauseRadioNetwork.h"
+#include "NR_HandoverPreparationInformation.h"
#include "openair3/ocp-gtpu/gtp_itf.h"
#include "openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.h"
#include "lib/f1ap_interface_management.h"
@@ -172,8 +173,6 @@ void f1_setup_response(const f1ap_setup_resp_t *resp)
AssertFatal(mac->f1_config.setup_resp != NULL, "out of memory\n");
// Copy F1AP message
*mac->f1_config.setup_resp = cp_f1ap_setup_response(resp);
- // free F1AP message after copy
- free_f1ap_setup_response(resp);
NR_SCHED_UNLOCK(&mac->sched_lock);
// NOTE: Before accepting any UEs, we should initialize the UE states.
@@ -237,8 +236,16 @@ static int handle_ue_context_srbs_setup(NR_UE_info_t *UE,
(*resp_srbs)[i].srb_id = srb->srb_id;
(*resp_srbs)[i].lcid = c.lcid;
- int ret = ASN_SEQUENCE_ADD(&cellGroupConfig->rlc_BearerToAddModList->list, rlc_BearerConfig);
- DevAssert(ret == 0);
+ if (rlc_BearerConfig->logicalChannelIdentity == 1) {
+ // CU asks to add SRB1: when creating a cellGroupConfig, we always add it
+ // (see get_initial_cellGroupConfig())
+ const struct NR_CellGroupConfig__rlc_BearerToAddModList *addmod = cellGroupConfig->rlc_BearerToAddModList;
+ DevAssert(addmod->list.count >= 1 && addmod->list.array[0]->logicalChannelIdentity == 1);
+ ASN_STRUCT_FREE(asn_DEF_NR_RLC_BearerConfig, rlc_BearerConfig);
+ } else {
+ int ret = ASN_SEQUENCE_ADD(&cellGroupConfig->rlc_BearerToAddModList->list, rlc_BearerConfig);
+ DevAssert(ret == 0);
+ }
}
return srbs_len;
}
@@ -375,6 +382,33 @@ static int handle_ue_context_drbs_release(NR_UE_info_t *UE,
return drbs_len;
}
+static NR_UE_NR_Capability_t *get_nr_cap(const NR_UE_CapabilityRAT_ContainerList_t *clist)
+{
+ for (int i = 0; i < clist->list.count; i++) {
+ const NR_UE_CapabilityRAT_Container_t *c = clist->list.array[i];
+ if (c->rat_Type != NR_RAT_Type_nr) {
+ LOG_W(NR_MAC, "ignoring capability of type %ld\n", c->rat_Type);
+ continue;
+ }
+
+ NR_UE_NR_Capability_t *cap = NULL;
+ asn_dec_rval_t dec_rval = uper_decode(NULL,
+ &asn_DEF_NR_UE_NR_Capability,
+ (void **)&cap,
+ c->ue_CapabilityRAT_Container.buf,
+ c->ue_CapabilityRAT_Container.size,
+ 0,
+ 0);
+ if (dec_rval.code != RC_OK) {
+ LOG_W(NR_MAC, "cannot decode NR UE capability, ignoring\n");
+ ASN_STRUCT_FREE(asn_DEF_NR_UE_NR_Capability, cap);
+ continue;
+ }
+ return cap;
+ }
+ return NULL;
+}
+
static NR_UE_NR_Capability_t *get_ue_nr_cap(int rnti, uint8_t *buf, uint32_t len)
{
if (buf == NULL || len == 0)
@@ -387,27 +421,37 @@ static NR_UE_NR_Capability_t *get_ue_nr_cap(int rnti, uint8_t *buf, uint32_t len
return NULL;
}
- NR_UE_NR_Capability_t *cap = NULL;
- for (int i = 0; i < clist->list.count; i++) {
- const NR_UE_CapabilityRAT_Container_t *c = clist->list.array[i];
- if (cap != NULL || c->rat_Type != NR_RAT_Type_nr) {
- LOG_W(NR_MAC, "UE RNTI %04x: ignoring capability of type %ld\n", rnti, c->rat_Type);
- continue;
- }
+ NR_UE_NR_Capability_t *cap = get_nr_cap(clist);
+ ASN_STRUCT_FREE(asn_DEF_NR_UE_CapabilityRAT_ContainerList, clist);
+ return cap;
+}
- asn_dec_rval_t dec_rval = uper_decode(NULL,
- &asn_DEF_NR_UE_NR_Capability,
- (void **)&cap,
- c->ue_CapabilityRAT_Container.buf,
- c->ue_CapabilityRAT_Container.size,
- 0,
- 0);
- if (dec_rval.code != RC_OK) {
- LOG_W(NR_MAC, "cannot decode NR UE capabilities of UE RNTI %04x, ignoring NR capability\n", rnti);
- cap = NULL;
- continue;
- }
+/* \brief return UE capabilties from HandoverPreparationInformation.
+ *
+ * The HandoverPreparationInformation contains more, but for the moment, let's
+ * keep it simple and only handle that. The function asserts if other IEs are
+ * present. */
+static NR_UE_NR_Capability_t *get_ue_nr_cap_from_ho_prep_info(uint8_t *buf, uint32_t len)
+{
+ if (buf == NULL || len == 0)
+ return NULL;
+ NR_HandoverPreparationInformation_t *hpi = NULL;
+ asn_dec_rval_t dec_rval = uper_decode_complete(NULL, &asn_DEF_NR_HandoverPreparationInformation, (void **)&hpi, buf, len);
+ if (dec_rval.code != RC_OK) {
+ LOG_W(NR_MAC, "cannot decode HandoverPreparationInformation, ignoring capabilities\n");
+ return NULL;
}
+ NR_UE_NR_Capability_t *cap = NULL;
+ if (hpi->criticalExtensions.present != NR_HandoverPreparationInformation__criticalExtensions_PR_c1
+ || hpi->criticalExtensions.choice.c1 == NULL
+ || hpi->criticalExtensions.choice.c1->present
+ != NR_HandoverPreparationInformation__criticalExtensions__c1_PR_handoverPreparationInformation
+ || hpi->criticalExtensions.choice.c1->choice.handoverPreparationInformation == NULL) {
+ } else {
+ const NR_HandoverPreparationInformation_IEs_t *hpi_ie = hpi->criticalExtensions.choice.c1->choice.handoverPreparationInformation;
+ cap = get_nr_cap(&hpi_ie->ue_CapabilityRAT_List);
+ }
+ ASN_STRUCT_FREE(asn_DEF_NR_HandoverPreparationInformation, hpi);
return cap;
}
@@ -423,6 +467,40 @@ NR_CellGroupConfig_t *clone_CellGroupConfig(const NR_CellGroupConfig_t *orig)
return cloned;
}
+static NR_UE_info_t *create_new_UE(gNB_MAC_INST *mac, uint32_t cu_id)
+{
+ int CC_id = 0;
+ const NR_COMMON_channels_t *cc = &mac->common_channels[CC_id];
+ rnti_t rnti;
+ bool found = nr_mac_get_new_rnti(&mac->UE_info, cc->ra, sizeofArray(cc->ra), &rnti);
+ if (!found)
+ return NULL;
+
+ NR_UE_info_t* UE = add_new_nr_ue(mac, rnti, NULL);
+ if (!UE)
+ return NULL;
+
+ f1_ue_data_t new_ue_data = {.secondary_ue = cu_id};
+ du_add_f1_ue_data(rnti, &new_ue_data);
+
+ const NR_ServingCellConfigCommon_t *scc = mac->common_channels[CC_id].ServingCellConfigCommon;
+ const NR_ServingCellConfig_t *sccd = mac->common_channels[CC_id].pre_ServingCellConfig;
+ NR_CellGroupConfig_t *cellGroupConfig = get_initial_cellGroupConfig(UE->uid, scc, sccd, &mac->radio_config);
+ cellGroupConfig->spCellConfig->reconfigurationWithSync = get_reconfiguration_with_sync(UE->rnti, UE->uid, scc);
+ // note: we don't pass the cellGroupConfig to add_new_nr_ue() because we need
+ // the uid to create the CellGroupConfig (which is in the UE context created
+ // by add_new_nr_ue(); it's a kind of chicken-and-egg problem), so below we
+ // complete the UE context with the information that add_new_nr_ue() would
+ // have added
+ UE->Msg4_MsgB_ACKed = true;
+ UE->CellGroup = cellGroupConfig;
+
+ nr_rlc_activate_srb0(UE->rnti, UE, NULL);
+ nr_mac_prepare_ra_ue(mac, rnti, UE->CellGroup);
+ /* SRB1 is added to RLC and MAC in the handler later */
+ return UE;
+}
+
void ue_context_setup_request(const f1ap_ue_context_setup_t *req)
{
gNB_MAC_INST *mac = RC.nrmac[0];
@@ -432,19 +510,34 @@ void ue_context_setup_request(const f1ap_ue_context_setup_t *req)
.gNB_DU_ue_id = req->gNB_DU_ue_id,
};
+ /* gNB-DU UE ID is optional. As of now, the F1AP module fills -1 (on uint, so
+ * 0xffffffff), but the DU uses the RNTI, so check if it's a legal RNTI in
+ * which case we consider; if not, assume no DU UE ID given */
+ bool ue_id_provided = resp.gNB_DU_ue_id > 0 && resp.gNB_DU_ue_id < 0xffff;
+
NR_UE_NR_Capability_t *ue_cap = NULL;
if (req->cu_to_du_rrc_information != NULL) {
- AssertFatal(req->cu_to_du_rrc_information->cG_ConfigInfo == NULL, "CG-ConfigInfo not handled\n");
- ue_cap = get_ue_nr_cap(req->gNB_DU_ue_id,
- req->cu_to_du_rrc_information->uE_CapabilityRAT_ContainerList,
- req->cu_to_du_rrc_information->uE_CapabilityRAT_ContainerList_length);
- AssertFatal(req->cu_to_du_rrc_information->measConfig == NULL, "MeasConfig not handled\n");
+ const cu_to_du_rrc_information_t *cu2du = req->cu_to_du_rrc_information;
+ AssertFatal(cu2du->cG_ConfigInfo == NULL, "CG-ConfigInfo not handled\n");
+ if (cu2du->handoverPreparationInfo != NULL) {
+ ue_cap = get_ue_nr_cap_from_ho_prep_info(cu2du->handoverPreparationInfo, cu2du->handoverPreparationInfo_length);
+ } else if (cu2du->uE_CapabilityRAT_ContainerList != NULL) {
+ ue_cap = get_ue_nr_cap(req->gNB_DU_ue_id, cu2du->uE_CapabilityRAT_ContainerList, cu2du->uE_CapabilityRAT_ContainerList_length);
+ }
+ AssertFatal(cu2du->measConfig == NULL, "MeasConfig not handled\n");
}
NR_SCHED_LOCK(&mac->sched_lock);
- NR_UE_info_t *UE = find_nr_UE(&RC.nrmac[0]->UE_info, req->gNB_DU_ue_id);
- AssertFatal(UE, "did not find UE with RNTI %04x, but UE Context Setup Failed not implemented\n", req->gNB_DU_ue_id);
+ NR_UE_info_t *UE = NULL;
+ if (!ue_id_provided) {
+ UE = create_new_UE(mac, req->gNB_CU_ue_id);
+ resp.gNB_DU_ue_id = UE->rnti;
+ resp.crnti = &UE->rnti;
+ } else {
+ UE = find_nr_UE(&mac->UE_info, req->gNB_DU_ue_id);
+ }
+ AssertFatal(UE, "no UE found or could not be created, but UE Context Setup Failed not implemented\n");
NR_CellGroupConfig_t *new_CellGroup = clone_CellGroupConfig(UE->CellGroup);
@@ -476,6 +569,14 @@ void ue_context_setup_request(const f1ap_ue_context_setup_t *req)
update_cellGroupConfig(new_CellGroup, UE->uid, UE->capability, &mac->radio_config, scc);
}
+ if (!ue_id_provided) {
+ /* new UE: tell the UE to reestablish RLC */
+ struct NR_CellGroupConfig__rlc_BearerToAddModList *addmod = new_CellGroup->rlc_BearerToAddModList;
+ for (int i = 0; i < addmod->list.count; ++i) {
+ asn1cCallocOne(addmod->list.array[i]->reestablishRLC, NR_RLC_BearerConfig__reestablishRLC_true);
+ }
+ }
+
resp.du_to_cu_rrc_information = calloc(1, sizeof(du_to_cu_rrc_information_t));
AssertFatal(resp.du_to_cu_rrc_information != NULL, "out of memory\n");
resp.du_to_cu_rrc_information->cellGroupConfig = calloc(1,1024);
@@ -588,6 +689,11 @@ void ue_context_modification_request(const f1ap_ue_context_modif_req_t *req)
} else {
ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, new_CellGroup); // we actually don't need it
}
+
+ if (req->transm_action_ind != NULL) {
+ AssertFatal(*req->transm_action_ind == TransmActionInd_STOP, "Transmission Action Indicator restart not handled yet\n");
+ nr_transmission_action_indicator_stop(mac, UE);
+ }
NR_SCHED_UNLOCK(&mac->sched_lock);
/* some sanity checks, since we use the same type for request and response */
@@ -728,7 +834,8 @@ void dl_rrc_message_transfer(const f1ap_dl_rrc_message_t *dl_rrc)
* the CellGroupConfig. Below, we trigger a timer, and the CellGroupConfig
* will be applied after its expiry in nr_mac_apply_cellgroup().*/
NR_SCHED_LOCK(&mac->sched_lock);
- nr_mac_enable_ue_rrc_processing_timer(mac, UE, /* apply_cellGroup = */ true);
+ int delay = nr_mac_get_reconfig_delay_slots(UE->current_UL_BWP.scs);
+ nr_mac_interrupt_ue_transmission(mac, UE, FOLLOW_INSYNC_RECONFIG, delay);
NR_SCHED_UNLOCK(&mac->sched_lock);
UE->expect_reconfiguration = false;
/* Re-establish RLC for all remaining bearers */
diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_rrc_ul_f1ap.c b/openair2/LAYER2/NR_MAC_gNB/mac_rrc_ul_f1ap.c
index 2af81d0cfd..6d8f7a7115 100644
--- a/openair2/LAYER2/NR_MAC_gNB/mac_rrc_ul_f1ap.c
+++ b/openair2/LAYER2/NR_MAC_gNB/mac_rrc_ul_f1ap.c
@@ -112,6 +112,10 @@ static void ue_context_setup_response_f1ap(const f1ap_ue_context_setup_t *req, c
for (int i = 0; i < f1ap_msg->drbs_to_be_setup_length; ++i)
f1ap_msg->drbs_to_be_setup[i] = resp->drbs_to_be_setup[i];
}
+ if (resp->crnti) {
+ f1ap_msg->crnti = calloc(1, sizeof(uint16_t));
+ *f1ap_msg->crnti = *resp->crnti;
+ }
f1ap_msg->du_to_cu_rrc_information = malloc(sizeof(*resp->du_to_cu_rrc_information));
AssertFatal(f1ap_msg->du_to_cu_rrc_information != NULL, "out of memory\n");
diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h
index aa07e8f474..e0cf53c5a0 100644
--- a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h
+++ b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h
@@ -43,6 +43,7 @@
#include
#include
#include "common/utils/ds/seq_arr.h"
+#include "common/utils/nr/nr_common.h"
#define NR_SCHED_LOCK(lock) \
do { \
@@ -664,8 +665,8 @@ typedef struct {
NR_list_t retrans_ul_harq;
NR_UE_mac_ce_ctrl_t UE_mac_ce_ctrl; // MAC CE related information
- /// Timer for RRC processing procedures
- uint32_t rrc_processing_timer;
+ /// Timer for RRC processing procedures and transmission activity
+ NR_timer_t transm_interrupt;
/// sri, ul_ri and tpmi based on SRS
nr_srs_feedback_t srs_feedback;
@@ -738,6 +739,8 @@ typedef struct nr_mac_rrc_ul_if_s {
initial_ul_rrc_message_transfer_func_t initial_ul_rrc_message_transfer;
} nr_mac_rrc_ul_if_t;
+typedef enum interrupt_followup_action { FOLLOW_INSYNC, FOLLOW_INSYNC_RECONFIG, FOLLOW_OUTOFSYNC } interrupt_followup_action_t;
+
/*! \brief UE list used by gNB to order UEs/CC for scheduling*/
typedef struct {
rnti_t rnti;
@@ -756,7 +759,7 @@ typedef struct {
/// reestablishRLC has to be signaled in RRCreconfiguration
bool reestablish_rlc;
NR_CellGroupConfig_t *reconfigCellGroup;
- bool apply_cellgroup;
+ interrupt_followup_action_t interrupt_action;
NR_UE_NR_Capability_t *capability;
// UE selected beam index
uint8_t UE_beam_index;
diff --git a/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c b/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c
index 0cccdbcb6c..e0ac130279 100644
--- a/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c
+++ b/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c
@@ -157,6 +157,7 @@ void e1_bearer_context_setup(const e1ap_bearer_setup_req_t *req)
AssertFatal(req_pdu->numDRB2Setup == 1, "can only handle one DRB per PDU session\n");
resp_pdu->numDRBSetup = req_pdu->numDRB2Setup;
const DRB_nGRAN_to_setup_t *req_drb = &req_pdu->DRBnGRanList[0];
+ AssertFatal(req_drb->numQosFlow2Setup == 1, "can only handle one QoS Flow per DRB\n");
DRB_nGRAN_setup_t *resp_drb = &resp_pdu->DRBnGRanList[0];
resp_drb->id = req_drb->id;
resp_drb->numQosFlowSetup = req_drb->numQosFlow2Setup;
@@ -168,7 +169,7 @@ void e1_bearer_context_setup(const e1ap_bearer_setup_req_t *req)
// GTP tunnel for N3/to core
gtpv1u_gnb_create_tunnel_resp_t resp_n3 = {0};
- int qfi = 0; // put PDU session marker
+ int qfi = req_drb->qosFlows[0].qfi;
int ret = drb_gtpu_create(n3inst,
cu_up_ue_id,
req_drb->id,
diff --git a/openair2/LAYER2/nr_rlc/nr_rlc_entity_am.c b/openair2/LAYER2/nr_rlc/nr_rlc_entity_am.c
index 480338fd84..8c85075598 100644
--- a/openair2/LAYER2/nr_rlc/nr_rlc_entity_am.c
+++ b/openair2/LAYER2/nr_rlc/nr_rlc_entity_am.c
@@ -28,6 +28,7 @@
#include "LOG/log.h"
#include "common/utils/time_stat.h"
+#include "common/utils/assertions.h"
/* for a given SDU/SDU segment, computes the corresponding PDU header size */
static int compute_pdu_header_size(nr_rlc_entity_am_t *entity,
diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.c b/openair2/RRC/NR/MESSAGES/asn1_msg.c
index 8025fb2243..68a8c49774 100644
--- a/openair2/RRC/NR/MESSAGES/asn1_msg.c
+++ b/openair2/RRC/NR/MESSAGES/asn1_msg.c
@@ -80,6 +80,8 @@
#include "NR_PCCH-Message.h"
#include "NR_PagingRecord.h"
#include "NR_UE-CapabilityRequestFilterNR.h"
+#include "NR_HandoverPreparationInformation.h"
+#include "NR_HandoverPreparationInformation-IEs.h"
#include "common/utils/nr/nr_common.h"
#if defined(NR_Rel16)
#include "NR_SCS-SpecificCarrier.h"
@@ -1373,3 +1375,42 @@ int do_NR_Paging(uint8_t Mod_id, uint8_t *buffer, uint32_t tmsi)
return((enc_rval.encoded+7)/8);
}
+
+/* \brief generate HandoverPreparationInformation to be sent to the DU for
+ * handover. Takes uecap_buf in encoded form as (1) this is the form present at
+ * the CU already (2) we have to clone this information anyway, so can take it
+ * in encoded form which we decode + add to the handoverPreparationInformation */
+int do_NR_HandoverPreparationInformation(const uint8_t *uecap_buf, int uecap_buf_size, uint8_t *buf, int buf_size)
+{
+ NR_HandoverPreparationInformation_t *hpi = calloc(1, sizeof(*hpi));
+ AssertFatal(hpi != NULL, "out of memory\n");
+ hpi->criticalExtensions.present = NR_HandoverPreparationInformation__criticalExtensions_PR_c1;
+ hpi->criticalExtensions.choice.c1 = calloc(1, sizeof(*hpi->criticalExtensions.choice.c1));
+ AssertFatal(hpi->criticalExtensions.choice.c1 != NULL, "out of memory\n");
+ hpi->criticalExtensions.choice.c1->present =
+ NR_HandoverPreparationInformation__criticalExtensions__c1_PR_handoverPreparationInformation;
+ NR_HandoverPreparationInformation_IEs_t *hpi_ie = calloc(1, sizeof(*hpi_ie));
+ AssertFatal(hpi_ie != NULL, "out of memory\n");
+ hpi->criticalExtensions.choice.c1->choice.handoverPreparationInformation = hpi_ie;
+
+ NR_UE_CapabilityRAT_ContainerList_t *list = NULL;
+ asn_dec_rval_t dec_rval =
+ uper_decode_complete(NULL, &asn_DEF_NR_UE_CapabilityRAT_ContainerList, (void **)&list, uecap_buf, uecap_buf_size);
+ if (dec_rval.code == RC_OK) {
+ hpi_ie->ue_CapabilityRAT_List = *list;
+ free(list); /* list itself is not needed, members below will be freed in ASN_STRUCT_FREE */
+ } else {
+ /* problem with decoding, don't put a capability */
+ ASN_STRUCT_FREE(asn_DEF_NR_UE_CapabilityRAT_ContainerList, list);
+ list = NULL;
+ }
+
+ if (LOG_DEBUGFLAG(DEBUG_ASN1))
+ xer_fprint(stdout, &asn_DEF_NR_HandoverPreparationInformation, hpi);
+
+ asn_enc_rval_t enc_rval = uper_encode_to_buffer(&asn_DEF_NR_HandoverPreparationInformation, NULL, hpi, buf, buf_size);
+ AssertFatal(enc_rval.encoded > 0, "ASN1 message encoding failed (%s, %lu)!\n", enc_rval.failed_type->name, enc_rval.encoded);
+
+ ASN_STRUCT_FREE(asn_DEF_NR_HandoverPreparationInformation, hpi);
+ return (enc_rval.encoded + 7) / 8;
+}
diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.h b/openair2/RRC/NR/MESSAGES/asn1_msg.h
index 8609e20328..2f0b4febf8 100644
--- a/openair2/RRC/NR/MESSAGES/asn1_msg.h
+++ b/openair2/RRC/NR/MESSAGES/asn1_msg.h
@@ -109,6 +109,8 @@ int do_RRCSetupComplete(uint8_t *buffer,
const int dedicatedInfoNASLength,
const char *dedicatedInfoNAS);
+int do_NR_HandoverPreparationInformation(const uint8_t *uecap_buf, int uecap_buf_size, uint8_t *buf, int buf_size);
+
int do_RRCSetupRequest(uint8_t *buffer, size_t buffer_size, uint8_t *rv);
int do_NR_RRCReconfigurationComplete_for_nsa(uint8_t *buffer, size_t buffer_size, NR_RRC_TransactionIdentifier_t Transaction_id);
diff --git a/openair2/RRC/NR/mac_rrc_dl_f1ap.c b/openair2/RRC/NR/mac_rrc_dl_f1ap.c
index e2e094d866..70d36a144f 100644
--- a/openair2/RRC/NR/mac_rrc_dl_f1ap.c
+++ b/openair2/RRC/NR/mac_rrc_dl_f1ap.c
@@ -79,8 +79,8 @@ static void ue_context_setup_request_f1ap(sctp_assoc_t assoc_id, const f1ap_ue_c
AssertFatal(f1ap_msg->cu_to_du_rrc_information != NULL, "out of memory\n");
AssertFatal(req->cu_to_du_rrc_information->cG_ConfigInfo == NULL && req->cu_to_du_rrc_information->cG_ConfigInfo_length == 0, "cg_ConfigInfo not implemented\n");
AssertFatal(req->cu_to_du_rrc_information->measConfig == NULL && req->cu_to_du_rrc_information->measConfig_length == 0, "cg_ConfigInfo not implemented\n");
+ const cu_to_du_rrc_information_t *du2cu_req = req->cu_to_du_rrc_information;
if (req->cu_to_du_rrc_information->uE_CapabilityRAT_ContainerList != NULL) {
- const cu_to_du_rrc_information_t *du2cu_req = req->cu_to_du_rrc_information;
cu_to_du_rrc_information_t* du2cu_new = f1ap_msg->cu_to_du_rrc_information;
DevAssert(du2cu_req->uE_CapabilityRAT_ContainerList_length > 0);
du2cu_new->uE_CapabilityRAT_ContainerList_length = du2cu_req->uE_CapabilityRAT_ContainerList_length;
@@ -88,6 +88,13 @@ static void ue_context_setup_request_f1ap(sctp_assoc_t assoc_id, const f1ap_ue_c
AssertFatal(du2cu_new->uE_CapabilityRAT_ContainerList != NULL, "out of memory\n");
memcpy(du2cu_new->uE_CapabilityRAT_ContainerList, du2cu_req->uE_CapabilityRAT_ContainerList, du2cu_new->uE_CapabilityRAT_ContainerList_length);
}
+ if (req->cu_to_du_rrc_information->handoverPreparationInfo != NULL) {
+ cu_to_du_rrc_information_t *du2cu_new = f1ap_msg->cu_to_du_rrc_information;
+ DevAssert(du2cu_req->handoverPreparationInfo_length > 0);
+ du2cu_new->handoverPreparationInfo_length = du2cu_req->handoverPreparationInfo_length;
+ du2cu_new->handoverPreparationInfo = malloc_or_fail(du2cu_new->handoverPreparationInfo_length);
+ memcpy(du2cu_new->handoverPreparationInfo, du2cu_req->handoverPreparationInfo, du2cu_new->handoverPreparationInfo_length);
+ }
}
if (req->drbs_to_be_setup_length > 0) {
int n = req->drbs_to_be_setup_length;
@@ -161,6 +168,11 @@ static void ue_context_modification_request_f1ap(sctp_assoc_t assoc_id, const f1
f1ap_msg->rrc_container_length = req->rrc_container_length;
memcpy(f1ap_msg->rrc_container, req->rrc_container, req->rrc_container_length);
}
+ if (req->transm_action_ind != NULL) {
+ f1ap_msg->transm_action_ind = malloc(sizeof *f1ap_msg->transm_action_ind);
+ AssertFatal(f1ap_msg->transm_action_ind != NULL, "out of memory\n");
+ *f1ap_msg->transm_action_ind = *req->transm_action_ind;
+ }
itti_send_msg_to_task(TASK_CU_F1, 0, msg);
}
diff --git a/openair2/RRC/NR/nr_rrc_config.c b/openair2/RRC/NR/nr_rrc_config.c
index 201a0e84d4..dab37f6f1a 100644
--- a/openair2/RRC/NR/nr_rrc_config.c
+++ b/openair2/RRC/NR/nr_rrc_config.c
@@ -3293,46 +3293,8 @@ NR_CellGroupConfig_t *get_default_secondaryCellGroup(const NR_ServingCellConfigC
secondaryCellGroup->spCellConfig->reconfigurationWithSync =
calloc(1, sizeof(*secondaryCellGroup->spCellConfig->reconfigurationWithSync));
- NR_ReconfigurationWithSync_t *reconfigurationWithSync = secondaryCellGroup->spCellConfig->reconfigurationWithSync;
- reconfigurationWithSync->spCellConfigCommon = clone_ServingCellConfigCommon(servingcellconfigcommon);
- reconfigurationWithSync->newUE_Identity =
- (get_softmodem_params()->phy_test == 1) ? 0x1234 : (taus() & 0xffff);
- reconfigurationWithSync->t304 = NR_ReconfigurationWithSync__t304_ms2000;
- reconfigurationWithSync->rach_ConfigDedicated = NULL;
- reconfigurationWithSync->ext1 = NULL;
-
- // For 2-step contention-free random access procedure
- reconfigurationWithSync->rach_ConfigDedicated = calloc(1, sizeof(*reconfigurationWithSync->rach_ConfigDedicated));
- reconfigurationWithSync->rach_ConfigDedicated->present = NR_ReconfigurationWithSync__rach_ConfigDedicated_PR_uplink;
- reconfigurationWithSync->rach_ConfigDedicated->choice.uplink = calloc(1, sizeof(struct NR_RACH_ConfigDedicated));
- reconfigurationWithSync->rach_ConfigDedicated->choice.uplink->cfra = calloc(1, sizeof(struct NR_CFRA));
- reconfigurationWithSync->rach_ConfigDedicated->choice.uplink->ra_Prioritization = NULL;
- reconfigurationWithSync->rach_ConfigDedicated->choice.uplink->cfra->occasions = calloc(1, sizeof(struct NR_CFRA__occasions));
- memcpy(&reconfigurationWithSync->rach_ConfigDedicated->choice.uplink->cfra->occasions->rach_ConfigGeneric,
- &servingcellconfigcommon->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->rach_ConfigGeneric,
- sizeof(NR_RACH_ConfigGeneric_t));
- asn1cCallocOne(reconfigurationWithSync->rach_ConfigDedicated->choice.uplink->cfra->occasions->ssb_perRACH_Occasion,
- NR_CFRA__occasions__ssb_perRACH_Occasion_one);
- reconfigurationWithSync->rach_ConfigDedicated->choice.uplink->cfra->resources.present = NR_CFRA__resources_PR_ssb;
- reconfigurationWithSync->rach_ConfigDedicated->choice.uplink->cfra->resources.choice.ssb =
- calloc(1, sizeof(struct NR_CFRA__resources__ssb));
- reconfigurationWithSync->rach_ConfigDedicated->choice.uplink->cfra->resources.choice.ssb->ra_ssb_OccasionMaskIndex = 0;
-
- int n_ssb = 0;
- struct NR_CFRA_SSB_Resource *ssbElem[64];
- for (int i = 0; i < 64; i++) {
- if ((bitmap >> (63 - i)) & 0x01) {
- ssbElem[n_ssb] = calloc(1, sizeof(struct NR_CFRA_SSB_Resource));
- ssbElem[n_ssb]->ssb = i;
- ssbElem[n_ssb]->ra_PreambleIndex = 63 - (uid % 64);
- asn1cSeqAdd(&secondaryCellGroup->spCellConfig->reconfigurationWithSync->rach_ConfigDedicated->choice.uplink->cfra->resources
- .choice.ssb->ssb_ResourceList.list,
- ssbElem[n_ssb]);
- n_ssb++;
- }
- }
-
- secondaryCellGroup->spCellConfig->reconfigurationWithSync->rach_ConfigDedicated->choice.uplink->cfra->ext1 = NULL;
+ rnti_t rnti = get_softmodem_params()->phy_test == 1 ? 0x1234 : (taus() & 0xffff);
+ secondaryCellGroup->spCellConfig->reconfigurationWithSync = get_reconfiguration_with_sync(rnti, uid, servingcellconfigcommon);
secondaryCellGroup->spCellConfig->rlf_TimersAndConstants =
calloc(1, sizeof(*secondaryCellGroup->spCellConfig->rlf_TimersAndConstants));
@@ -3602,3 +3564,43 @@ NR_CellGroupConfig_t *get_default_secondaryCellGroup(const NR_ServingCellConfigC
}
return secondaryCellGroup;
}
+
+NR_ReconfigurationWithSync_t *get_reconfiguration_with_sync(rnti_t rnti, uid_t uid, const NR_ServingCellConfigCommon_t *scc)
+{
+ NR_ReconfigurationWithSync_t *reconfigurationWithSync = calloc(1, sizeof(*reconfigurationWithSync));
+ reconfigurationWithSync->newUE_Identity = rnti;
+ reconfigurationWithSync->t304 = NR_ReconfigurationWithSync__t304_ms2000;
+ reconfigurationWithSync->rach_ConfigDedicated = NULL;
+ reconfigurationWithSync->ext1 = NULL;
+
+ reconfigurationWithSync->spCellConfigCommon = clone_ServingCellConfigCommon(scc);
+
+ reconfigurationWithSync->rach_ConfigDedicated = calloc(1, sizeof(*reconfigurationWithSync->rach_ConfigDedicated));
+ reconfigurationWithSync->rach_ConfigDedicated->present = NR_ReconfigurationWithSync__rach_ConfigDedicated_PR_uplink;
+ NR_RACH_ConfigDedicated_t *uplink = calloc(1, sizeof(*uplink));
+ reconfigurationWithSync->rach_ConfigDedicated->choice.uplink = uplink;
+ uplink->ra_Prioritization = NULL;
+ uplink->cfra = calloc(1, sizeof(struct NR_CFRA));
+ uplink->cfra->ext1 = NULL;
+ uplink->cfra->occasions = calloc(1, sizeof(struct NR_CFRA__occasions));
+ memcpy(&uplink->cfra->occasions->rach_ConfigGeneric,
+ &scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->rach_ConfigGeneric,
+ sizeof(NR_RACH_ConfigGeneric_t));
+ asn1cCallocOne(uplink->cfra->occasions->ssb_perRACH_Occasion, NR_CFRA__occasions__ssb_perRACH_Occasion_one);
+ uplink->cfra->resources.present = NR_CFRA__resources_PR_ssb;
+ uplink->cfra->resources.choice.ssb = calloc(1, sizeof(struct NR_CFRA__resources__ssb));
+ uplink->cfra->resources.choice.ssb->ra_ssb_OccasionMaskIndex = 0;
+
+ uint64_t bitmap = get_ssb_bitmap(scc);
+ for (int i = 0; i < 64; i++) {
+ if (((bitmap >> (63 - i)) & 0x01) == 0)
+ continue;
+
+ NR_CFRA_SSB_Resource_t *ssbElem = calloc(1, sizeof(*ssbElem));
+ ssbElem->ssb = i;
+ ssbElem->ra_PreambleIndex = 63 - (uid % 64);
+ asn1cSeqAdd(&uplink->cfra->resources.choice.ssb->ssb_ResourceList.list, ssbElem);
+ }
+
+ return reconfigurationWithSync;
+}
diff --git a/openair2/RRC/NR/nr_rrc_config.h b/openair2/RRC/NR/nr_rrc_config.h
index 87640834e5..ff94f2ab7e 100644
--- a/openair2/RRC/NR/nr_rrc_config.h
+++ b/openair2/RRC/NR/nr_rrc_config.h
@@ -102,4 +102,6 @@ NR_CellGroupConfig_t *get_default_secondaryCellGroup(const NR_ServingCellConfigC
const nr_mac_config_t *configuration,
int uid);
+NR_ReconfigurationWithSync_t *get_reconfiguration_with_sync(rnti_t rnti, uid_t uid, const NR_ServingCellConfigCommon_t *scc);
+
#endif
diff --git a/openair2/RRC/NR/nr_rrc_defs.h b/openair2/RRC/NR/nr_rrc_defs.h
index b5b27a6f4d..14d08e9c45 100644
--- a/openair2/RRC/NR/nr_rrc_defs.h
+++ b/openair2/RRC/NR/nr_rrc_defs.h
@@ -69,14 +69,6 @@
#include "intertask_interface.h"
-/* TODO: be sure this include is correct.
- * It solves a problem of compilation of the RRH GW,
- * issue #186.
- */
- #include "as_message.h"
-
- #include "commonDef.h"
-
#define PROTOCOL_NR_RRC_CTXT_UE_FMT PROTOCOL_CTXT_FMT
#define PROTOCOL_NR_RRC_CTXT_UE_ARGS(CTXT_Pp) PROTOCOL_NR_CTXT_ARGS(CTXT_Pp)
@@ -120,23 +112,6 @@ typedef struct nr_e_rab_param_s {
uint8_t xid; // transaction_id
} __attribute__ ((__packed__)) nr_e_rab_param_t;
-
-typedef struct HANDOVER_INFO_NR_s {
- uint8_t ho_prepare;
- uint8_t ho_complete;
- uint8_t modid_s; //module_idP of serving cell
- uint8_t modid_t; //module_idP of target cell
- uint8_t ueid_s; //UE index in serving cell
- uint8_t ueid_t; //UE index in target cell
-
- // NR not define at this moment
- //AS_Config_t as_config; /* these two parameters are taken from 36.331 section 10.2.2: HandoverPreparationInformation-r8-IEs */
- //AS_Context_t as_context; /* They are mandatory for HO */
-
- uint8_t buf[NR_RRC_BUF_SIZE]; /* ASN.1 encoded handoverCommandMessage */
- int size; /* size of above message in bytes */
-} NR_HANDOVER_INFO;
-
typedef struct nr_rrc_guami_s {
uint16_t mcc;
uint16_t mnc;
@@ -222,6 +197,9 @@ typedef enum {
RRC_UECAPABILITY_ENQUIRY,
} rrc_action_t;
+/* forward declaration */
+typedef struct nr_handover_context_s nr_handover_context_t;
+
typedef struct gNB_RRC_UE_s {
time_t last_seen; // last time this UE has been accessed
@@ -230,7 +208,7 @@ typedef struct gNB_RRC_UE_s {
NR_SRB_INFO_TABLE_ENTRY Srb[NR_NUM_SRB];
NR_MeasConfig_t *measConfig;
- NR_HANDOVER_INFO *handover_info;
+ nr_handover_context_t *ho_context;
NR_MeasResults_t *measResults;
bool as_security_active;
diff --git a/openair2/RRC/NR/nr_rrc_proto.h b/openair2/RRC/NR/nr_rrc_proto.h
index 9c3d276394..576a1f636a 100644
--- a/openair2/RRC/NR/nr_rrc_proto.h
+++ b/openair2/RRC/NR/nr_rrc_proto.h
@@ -130,4 +130,7 @@ void rrc_gNB_generate_UeContextModificationRequest(const gNB_RRC_INST *rrc,
int n_rel_drbs,
const f1ap_drb_to_be_released_t *rel_drbs);
+pdusession_level_qos_parameter_t *get_qos_characteristics(const int qfi, rrc_pdu_session_param_t *pduSession);
+f1ap_qos_characteristics_t get_qos_char_from_qos_flow_param(const pdusession_level_qos_parameter_t *qos_param);
+
#endif
diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c
index 5ab7c0ba12..1ff2747ec4 100644
--- a/openair2/RRC/NR/rrc_gNB.c
+++ b/openair2/RRC/NR/rrc_gNB.c
@@ -77,6 +77,7 @@
#include "rrc_gNB_NGAP.h"
#include "rrc_gNB_du.h"
+#include "rrc_gNB_mobility.h"
#include "rrc_gNB_GTPV1U.h"
@@ -98,6 +99,7 @@
#include "openair2/SDAP/nr_sdap/nr_sdap_entity.h"
#include "openair2/E1AP/e1ap.h"
#include "cucp_cuup_if.h"
+#include "lib/f1ap_interface_management.h"
#include "BIT_STRING.h"
#include "assertions.h"
@@ -112,16 +114,6 @@ extern RAN_CONTEXT_t RC;
mui_t rrc_gNB_mui = 0;
-// the assoc_id might be 0 (if the DU goes offline). Below helper macro to
-// print an error and return from the function in that case
-#define RETURN_IF_INVALID_ASSOC_ID(assoc_id) \
- { \
- if (assoc_id == 0) { \
- LOG_E(NR_RRC, "cannot send data: invalid assoc_id 0, DU offline\n"); \
- return; \
- } \
- }
-
///---------------------------------------------------------------------------------------------------------------///
///---------------------------------------------------------------------------------------------------------------///
@@ -481,6 +473,60 @@ static void rrc_gNB_process_RRCSetupComplete(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE
rrc_gNB_send_NGAP_NAS_FIRST_REQ(rrc, UE, rrcSetupComplete);
}
+static int rrc_gNB_encode_RRCReconfiguration(gNB_RRC_INST *rrc,
+ gNB_RRC_UE_t *UE,
+ uint8_t xid,
+ struct NR_RRCReconfiguration_v1530_IEs__dedicatedNAS_MessageList *nas_messages,
+ uint8_t *buf,
+ int max_len,
+ bool reestablish)
+{
+ NR_CellGroupConfig_t *cellGroupConfig = UE->masterCellGroup;
+ nr_rrc_du_container_t *du = get_du_for_ue(rrc, UE->rrc_ue_id);
+ DevAssert(du != NULL);
+ f1ap_served_cell_info_t *cell_info = &du->setup_req->cell[0].info;
+ NR_MeasConfig_t *measconfig = NULL;
+ if (du->mtc != NULL) {
+ int scs = get_ssb_scs(cell_info);
+ int band = get_dl_band(cell_info);
+ const NR_MeasTimingList_t *mtlist = du->mtc->criticalExtensions.choice.c1->choice.measTimingConf->measTiming;
+ const NR_MeasTiming_t *mt = mtlist->list.array[0];
+ const neighbour_cell_configuration_t *neighbour_config = get_neighbour_config(cell_info->nr_cellid);
+ seq_arr_t *neighbour_cells = NULL;
+ if (neighbour_config)
+ neighbour_cells = neighbour_config->neighbour_cells;
+
+ measconfig = get_MeasConfig(mt, band, scs, &rrc->measurementConfiguration, neighbour_cells);
+ }
+
+ if (UE->measConfig)
+ free_MeasConfig(UE->measConfig);
+
+ UE->measConfig = measconfig;
+
+ NR_SRB_ToAddModList_t *SRBs = createSRBlist(UE, reestablish);
+ NR_DRB_ToAddModList_t *DRBs = createDRBlist(UE, reestablish);
+
+ int size = do_RRCReconfiguration(UE,
+ buf,
+ max_len,
+ xid,
+ SRBs,
+ DRBs,
+ UE->DRB_ReleaseList,
+ NULL,
+ measconfig,
+ nas_messages,
+ cellGroupConfig);
+ LOG_DUMPMSG(NR_RRC, DEBUG_RRC, (char *)buf, size, "[MSG] RRC Reconfiguration\n");
+ freeSRBlist(SRBs);
+ freeDRBlist(DRBs);
+ ASN_STRUCT_FREE(asn_DEF_NR_DRB_ToReleaseList, UE->DRB_ReleaseList);
+ UE->DRB_ReleaseList = NULL;
+
+ return size;
+}
+
static void rrc_gNB_generate_dedicatedRRCReconfiguration(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue_p)
{
uint8_t xid = rrc_gNB_get_next_transaction_identifier(rrc->module_id);
@@ -517,51 +563,10 @@ static void rrc_gNB_generate_dedicatedRRCReconfiguration(gNB_RRC_INST *rrc, gNB_
for (int i = 0; i < ue_p->nb_of_pdusessions; i++)
clear_nas_pdu(&ue_p->pduSession[i].param.nas_pdu);
- NR_CellGroupConfig_t *cellGroupConfig = ue_p->masterCellGroup;
-
- const nr_rrc_du_container_t *du = get_du_for_ue(rrc, ue_p->rrc_ue_id);
- DevAssert(du != NULL);
- f1ap_served_cell_info_t *cell_info = &du->setup_req->cell[0].info;
- NR_MeasConfig_t *measconfig = NULL;
-
- if (du->mtc != NULL) {
- int scs = get_ssb_scs(cell_info);
- int band = get_dl_band(cell_info);
- const NR_MeasTimingList_t *mtlist = du->mtc->criticalExtensions.choice.c1->choice.measTimingConf->measTiming;
- const NR_MeasTiming_t *mt = mtlist->list.array[0];
- const neighbour_cell_configuration_t *neighbour_config = get_neighbour_config(cell_info->nr_cellid);
- seq_arr_t *neighbour_cells = NULL;
- if (neighbour_config)
- neighbour_cells = neighbour_config->neighbour_cells;
-
- measconfig = get_MeasConfig(mt, band, scs, &rrc->measurementConfiguration, neighbour_cells);
- }
-
- if (ue_p->measConfig)
- free_MeasConfig(ue_p->measConfig);
-
- ue_p->measConfig = measconfig;
-
uint8_t buffer[NR_RRC_BUF_SIZE] = {0};
- NR_SRB_ToAddModList_t *SRBs = createSRBlist(ue_p, false);
- NR_DRB_ToAddModList_t *DRBs = createDRBlist(ue_p, false);
-
- int size = do_RRCReconfiguration(ue_p,
- buffer,
- NR_RRC_BUF_SIZE,
- xid,
- SRBs,
- DRBs,
- ue_p->DRB_ReleaseList,
- NULL,
- measconfig,
- dedicatedNAS_MessageList,
- cellGroupConfig);
- LOG_DUMPMSG(NR_RRC, DEBUG_RRC, (char *)buffer, size, "[MSG] RRC Reconfiguration\n");
- freeSRBlist(SRBs);
- freeDRBlist(DRBs);
- ASN_STRUCT_FREE(asn_DEF_NR_DRB_ToReleaseList, ue_p->DRB_ReleaseList);
- ue_p->DRB_ReleaseList = NULL;
+ // TODO refactor dedicatedNAS_MessageList
+ int size = rrc_gNB_encode_RRCReconfiguration(rrc, ue_p, xid, dedicatedNAS_MessageList, buffer, sizeof(buffer), false);
+ DevAssert(size > 0 && size <= sizeof(buffer));
LOG_I(NR_RRC, "UE %d: Generate RRCReconfiguration (bytes %d, xid %d)\n", ue_p->rrc_ue_id, size, xid);
nr_rrc_transfer_protected_rrc_message(rrc, ue_p, DCCH, buffer, size);
@@ -905,6 +910,17 @@ static void rrc_gNB_process_RRCReestablishmentComplete(gNB_RRC_INST *rrc, gNB_RR
NR_RLC_BearerConfig__reestablishRLC_true);
}
+ /* TODO: remove a reconfigurationWithSync, we don't need it for
+ * reestablishment. The whole reason why this might be here is that we store
+ * the CellGroupConfig (after handover), and simply reuse it for
+ * reestablishment, instead of re-requesting the CellGroupConfig from the DU.
+ * Hence, add below hack; the solution would be to request the
+ * CellGroupConfig from the DU when doing reestablishment. */
+ if (cellGroupConfig->spCellConfig->reconfigurationWithSync != NULL) {
+ ASN_STRUCT_FREE(asn_DEF_NR_ReconfigurationWithSync, cellGroupConfig->spCellConfig->reconfigurationWithSync);
+ cellGroupConfig->spCellConfig->reconfigurationWithSync = NULL;
+ }
+
/* Re-establish SRB2 according to clause 5.3.5.6.3 of 3GPP TS 38.331
* (SRB1 is re-established with RRCReestablishment message)
*/
@@ -1026,9 +1042,18 @@ static void rrc_handle_RRCSetupRequest(gNB_RRC_INST *rrc,
return;
}
+ NR_CellGroupConfig_t *cellGroupConfig = NULL;
+ asn_dec_rval_t dec_rval = uper_decode_complete(NULL,
+ &asn_DEF_NR_CellGroupConfig,
+ (void **)&cellGroupConfig,
+ msg->du2cu_rrc_container,
+ msg->du2cu_rrc_container_length);
+ AssertFatal(dec_rval.code == RC_OK && dec_rval.consumed > 0, "Cell group config decode error\n");
+
gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
UE = &ue_context_p->ue_context;
UE->establishment_cause = rrcSetupRequest->establishmentCause;
+ UE->masterCellGroup = cellGroupConfig;
activate_srb(UE, 1);
rrc_gNB_generate_RRCSetup(0, msg->crnti, ue_context_p, msg->du2cu_rrc_container, msg->du2cu_rrc_container_length);
}
@@ -1048,6 +1073,20 @@ static const char *get_reestab_cause(NR_ReestablishmentCause_t c)
return "UNKNOWN Failure (ASN.1 decoder error?)";
}
+static rrc_gNB_ue_context_t *rrc_gNB_get_ue_context_source_cell(gNB_RRC_INST *rrc_instance_pP, long pci, rnti_t rntiP)
+{
+ rrc_gNB_ue_context_t *ue_context_p;
+ RB_FOREACH(ue_context_p, rrc_nr_ue_tree_s, &rrc_instance_pP->rrc_ue_head) {
+ gNB_RRC_UE_t *ue = &ue_context_p->ue_context;
+ if (!ue->ho_context || !ue->ho_context->source)
+ continue;
+ nr_ho_source_cu_t *source_ctx = ue->ho_context->source;
+ if (source_ctx->old_rnti == rntiP && source_ctx->du->setup_req->cell[0].info.nr_pci == pci)
+ return ue_context_p;
+ }
+ return NULL;
+}
+
static void rrc_handle_RRCReestablishmentRequest(gNB_RRC_INST *rrc,
sctp_assoc_t assoc_id,
const NR_RRCReestablishmentRequest_IEs_t *req,
@@ -1058,7 +1097,12 @@ static void rrc_handle_RRCReestablishmentRequest(gNB_RRC_INST *rrc,
const long physCellId = req->ue_Identity.physCellId;
long ngap_cause = NGAP_CAUSE_RADIO_NETWORK_UNSPECIFIED; /* cause in case of NGAP release req */
rrc_gNB_ue_context_t *ue_context_p = NULL;
- LOG_I(NR_RRC, "UE %04x physCellId %ld NR_RRCReestablishmentRequest cause %s\n", msg->crnti, physCellId, scause);
+ LOG_I(NR_RRC,
+ "Reestablishment RNTI %04x req C-RNTI %04lx physCellId %ld cause %s\n",
+ msg->crnti,
+ req->ue_Identity.c_RNTI,
+ physCellId,
+ scause);
const nr_rrc_du_container_t *du = get_du_by_assoc_id(rrc, assoc_id);
if (du == NULL) {
@@ -1093,13 +1137,47 @@ static void rrc_handle_RRCReestablishmentRequest(gNB_RRC_INST *rrc,
rnti_t old_rnti = req->ue_Identity.c_RNTI;
ue_context_p = rrc_gNB_get_ue_context_by_rnti(rrc, assoc_id, old_rnti);
if (ue_context_p == NULL) {
- LOG_E(NR_RRC, "NR_RRCReestablishmentRequest without UE context, fallback to RRC setup\n");
- goto fallback_rrc_setup;
+ ue_context_p = rrc_gNB_get_ue_context_source_cell(rrc, physCellId, old_rnti);
+ if (ue_context_p == NULL) {
+ LOG_E(NR_RRC, "NR_RRCReestablishmentRequest without UE context, fallback to RRC setup\n");
+ goto fallback_rrc_setup;
+ }
}
gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
+ /* should check phys cell ID to identify the correct cell */
const f1ap_served_cell_info_t *cell_info = &du->setup_req->cell[0].info;
- if (physCellId != cell_info->nr_pci) {
+ const f1ap_served_cell_info_t *previous_cell_info = get_cell_information_by_phycellId(physCellId);
+
+ nr_ho_source_cu_t *source_ctx = UE->ho_context ? UE->ho_context->source : NULL;
+ DevAssert(!source_ctx || source_ctx->du->setup_req->num_cells_available == 1);
+ bool ho_reestab_on_source = source_ctx && previous_cell_info->nr_cellid == source_ctx->du->setup_req->cell[0].info.nr_cellid;
+
+ if (ho_reestab_on_source) {
+ /* the UE came back on the source DU while doing handover, release at
+ * target DU and and update the association to the initial DU one */
+ LOG_W(NR_RRC, "handover for UE %d/RNTI %04x failed, rollback to original cell\n", UE->rrc_ue_id, UE->rnti);
+
+ // find the transaction of handover (the corresponding reconfig) and abort it
+ for (int i = 0; i < NR_RRC_TRANSACTION_IDENTIFIER_NUMBER; ++i) {
+ if (UE->xids[i] == RRC_DEDICATED_RECONF)
+ UE->xids[i] = RRC_ACTION_NONE;
+ }
+
+ source_ctx->ho_cancel(rrc, UE);
+
+ /* we need the original CellGroupConfig */
+ ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, UE->masterCellGroup);
+ UE->masterCellGroup = source_ctx->old_cellGroupConfig;
+ source_ctx->old_cellGroupConfig = NULL;
+
+ /* update to old DU assoc id -- RNTI + secondary DU UE ID further below */
+ f1_ue_data_t ue_data = cu_get_f1_ue_data(UE->rrc_ue_id);
+ ue_data.du_assoc_id = source_ctx->du->assoc_id;
+ cu_remove_f1_ue_data(UE->rrc_ue_id);
+ cu_add_f1_ue_data(UE->rrc_ue_id, &ue_data);
+ nr_rrc_finalize_ho(UE);
+ } else if (physCellId != cell_info->nr_pci) {
/* UE was moving from previous cell so quickly that RRCReestablishment for previous cell was received in this cell */
LOG_I(NR_RRC,
"RRC Reestablishment Request from different physCellId (%ld) than current physCellId (%d), fallback to RRC setup\n",
@@ -1159,7 +1237,7 @@ static void process_Periodical_Measurement_Report(gNB_RRC_UE_t *ue_ctxt, NR_Meas
measurementReport->criticalExtensions.choice.measurementReport = NULL;
}
-static void process_Event_Based_Measurement_Report(NR_ReportConfigNR_t *report, NR_MeasurementReport_t *measurementReport)
+static void process_Event_Based_Measurement_Report(gNB_RRC_UE_t *ue, NR_ReportConfigNR_t *report, NR_MeasurementReport_t *measurementReport)
{
NR_EventTriggerConfig_t *event_triggered = report->reportType.choice.eventTriggered;
@@ -1173,7 +1251,7 @@ static void process_Event_Based_Measurement_Report(NR_ReportConfigNR_t *report,
break;
case NR_EventTriggerConfig__eventId_PR_eventA3: {
- LOG_I(NR_RRC, "HO LOG: Event A3 Report - Neighbour Becomes Better than Serving!\n");
+ LOG_W(NR_RRC, "HO LOG: Event A3 Report - Neighbour Becomes Better than Serving!\n");
const NR_MeasResults_t *measResults = &measurementReport->criticalExtensions.choice.measurementReport->measResults;
for (int serving_cell_idx = 0; serving_cell_idx < measResults->measResultServingMOList.list.count; serving_cell_idx++) {
@@ -1212,13 +1290,13 @@ static void process_Event_Based_Measurement_Report(NR_ReportConfigNR_t *report,
neighbourCellId,
neighbourCellRSRP);
- const f1ap_served_cell_info_t *neighbour_cell_du_context = get_cell_information_by_phycellId(neighbourCellId);
- const f1ap_served_cell_info_t *serving_cell_du_context = get_cell_information_by_phycellId(servingCellId);
+ const f1ap_served_cell_info_t *neigh_cell = get_cell_information_by_phycellId(neighbourCellId);
+ const f1ap_served_cell_info_t *serving_cell = get_cell_information_by_phycellId(servingCellId);
const nr_neighbour_gnb_configuration_t *neighbour =
- get_neighbour_cell_information(serving_cell_du_context->nr_cellid, neighbourCellId);
+ get_neighbour_cell_information(serving_cell->nr_cellid, neighbourCellId);
// CU does not have f1 connection with neighbour cell context. So check does serving cell has this phyCellId as a
// neighbour.
- if (!neighbour_cell_du_context && neighbour) {
+ if (!neigh_cell && neighbour) {
// No F1 connection but static neighbour configuration is available
const nr_a3_event_t *a3_event_configuration = get_a3_configuration(neighbour->physicalCellId);
// Additional check - This part can be modified according to additional cell specific Handover Margin
@@ -1227,6 +1305,15 @@ static void process_Event_Based_Measurement_Report(NR_ReportConfigNR_t *report,
< (neighbourCellRSRP - servingCellRSRP))) {
LOG_D(NR_RRC, "HO LOG: Trigger N2 HO for the neighbour gnb: %u cell: %lu\n", neighbour->gNB_ID, neighbour->nrcell_id);
}
+ } else if (neigh_cell && neighbour) {
+ /* we know the cell and are connected to the DU! */
+ gNB_RRC_INST *rrc = RC.nrrrc[0];
+ nr_rrc_du_container_t *source_du = get_du_by_cell_id(rrc, serving_cell->nr_cellid);
+ DevAssert(source_du);
+ nr_rrc_du_container_t *target_du = get_du_by_cell_id(rrc, neigh_cell->nr_cellid);
+ nr_rrc_trigger_f1_ho(rrc, ue, source_du, target_du);
+ } else {
+ LOG_W(NR_RRC, "UE %d: received A3 event for stronger neighbor PCI %d, but no such neighbour in configuration\n", ue->rrc_ue_id, neighbourCellId);
}
}
@@ -1288,12 +1375,10 @@ static void rrc_gNB_process_MeasurementReport(gNB_RRC_UE_t *UE, NR_MeasurementRe
if (report_config->choice.reportConfigNR->reportType.present == NR_ReportConfigNR__reportType_PR_periodical)
return process_Periodical_Measurement_Report(UE, measurementReport);
- if (report_config->choice.reportConfigNR->reportType.present != NR_ReportConfigNR__reportType_PR_eventTriggered) {
- LOG_D(NR_RRC, "Incoming Report Type: %d is not supported! \n", report_config->choice.reportConfigNR->reportType.present);
- return;
- }
+ if (report_config->choice.reportConfigNR->reportType.present == NR_ReportConfigNR__reportType_PR_eventTriggered)
+ return process_Event_Based_Measurement_Report(UE, report_config->choice.reportConfigNR, measurementReport);
- process_Event_Based_Measurement_Report(report_config->choice.reportConfigNR, measurementReport);
+ LOG_E(NR_RRC, "Incoming Report Type: %d is not supported! \n", report_config->choice.reportConfigNR->reportType.present);
}
static int handle_rrcReestablishmentComplete(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, const NR_RRCReestablishmentComplete_t *cplt)
@@ -1545,6 +1630,14 @@ static void handle_rrcReconfigurationComplete(gNB_RRC_INST *rrc, gNB_RRC_UE_t *U
LOG_I(RRC, "UE %d: transaction %d still ongoing for action %d\n", UE->rrc_ue_id, i, UE->xids[i]);
}
}
+
+ if (UE->ho_context != NULL) {
+ LOG_A(NR_RRC, "handover for UE %d/RNTI %04x complete!\n", UE->rrc_ue_id, UE->rnti);
+ DevAssert(UE->ho_context->target != NULL);
+
+ UE->ho_context->target->ho_success(rrc, UE);
+ nr_rrc_finalize_ho(UE);
+ }
}
static void rrc_gNB_generate_UECapabilityEnquiry(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue)
@@ -1838,27 +1931,80 @@ static void rrc_CU_process_ue_context_setup_response(MessageDef *msg_p, instance
xer_fprint(stdout, &asn_DEF_NR_CellGroupConfig, UE->masterCellGroup);
if (resp->drbs_to_be_setup_length > 0) {
+ int num_drb = get_number_active_drbs(UE);
+ DevAssert(num_drb == 0 || num_drb == resp->drbs_to_be_setup_length);
+
/* Note: we would ideally check that SRB2 is acked, but at least LiteOn DU
* seems buggy and does not ack, so simply check that locally we activated */
AssertFatal(UE->Srb[1].Active && UE->Srb[2].Active, "SRBs 1 and 2 must be active during DRB Establishment");
store_du_f1u_tunnel(resp->drbs_to_be_setup, resp->drbs_to_be_setup_length, UE);
- e1_send_bearer_updates(rrc, UE, resp->drbs_to_be_setup_length, resp->drbs_to_be_setup);
+ if (num_drb == 0)
+ e1_send_bearer_updates(rrc, UE, resp->drbs_to_be_setup_length, resp->drbs_to_be_setup);
+ else
+ cuup_notify_reestablishment(rrc, UE);
}
- rrc_gNB_generate_dedicatedRRCReconfiguration(rrc, UE);
+ if (UE->ho_context == NULL) {
+ rrc_gNB_generate_dedicatedRRCReconfiguration(rrc, UE);
+ } else {
+ // case of handover
+ // handling of "target CU" information
+ DevAssert(UE->ho_context->target != NULL);
+ DevAssert(resp->crnti != NULL);
+ UE->ho_context->target->du_ue_id = resp->gNB_DU_ue_id;
+ UE->ho_context->target->new_rnti = *resp->crnti;
+
+ uint8_t xid = rrc_gNB_get_next_transaction_identifier(0);
+ UE->xids[xid] = RRC_DEDICATED_RECONF;
+ uint8_t buffer[NR_RRC_BUF_SIZE] = {0};
+ int size = rrc_gNB_encode_RRCReconfiguration(rrc, UE, xid, NULL, buffer, sizeof(buffer), true);
+ DevAssert(size > 0 && size <= sizeof(buffer));
+
+ // TODO N2 38.413 sec 9.3.1.21: admitted PDU sessions (in UE), Target to
+ // Source Transparent Container (9.3.1.21) which encodes the RRC
+ // reconfiguration above
+ UE->ho_context->target->ho_req_ack(rrc, UE, buffer, size);
+ }
}
-static void rrc_CU_process_ue_context_release_request(MessageDef *msg_p)
+static void rrc_CU_process_ue_context_release_request(MessageDef *msg_p, sctp_assoc_t assoc_id)
{
const int instance = 0;
f1ap_ue_context_release_req_t *req = &F1AP_UE_CONTEXT_RELEASE_REQ(msg_p);
gNB_RRC_INST *rrc = RC.nrrrc[instance];
rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(rrc, req->gNB_CU_ue_id);
+ // TODO what happens if no AMF connected? should also handle, set an_release true
if (!ue_context_p) {
LOG_E(RRC, "could not find UE context for CU UE ID %u, aborting transaction\n", req->gNB_CU_ue_id);
+ // TODO just request the DU to release to make it happy
return;
}
+ gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
+ if (UE->ho_context != NULL) {
+ nr_ho_source_cu_t *source_ctx = UE->ho_context->source;
+ bool from_source_du = source_ctx && source_ctx->du->assoc_id == assoc_id;
+ if (from_source_du) {
+ // we received release request from the source DU, but HO is still
+ // ongoing; free the UE, and remove the HO context.
+ LOG_W(NR_RRC, "UE %d: release request from source DU ID %ld during HO, marking HO as complete\n", UE->rrc_ue_id, source_ctx->du->setup_req->gNB_DU_id);
+ RETURN_IF_INVALID_ASSOC_ID(source_ctx->du->assoc_id);
+ f1ap_ue_context_release_cmd_t cmd = {
+ .gNB_CU_ue_id = UE->rrc_ue_id,
+ .gNB_DU_ue_id = source_ctx->du_ue_id,
+ .cause = F1AP_CAUSE_RADIO_NETWORK,
+ .cause_value = 5, // 5 = F1AP_CauseRadioNetwork_interaction_with_other_procedure
+ .srb_id = DCCH,
+ };
+ rrc->mac_rrc.ue_context_release_command(assoc_id, &cmd);
+ nr_rrc_finalize_ho(UE);
+ return;
+ }
+ // if we receive the release request from the target DU (regardless if
+ // successful), we assume it is "genuine" and ask the AMF to release
+ nr_rrc_finalize_ho(UE);
+ }
+
/* TODO: marshall types correctly */
LOG_I(NR_RRC, "received UE Context Release Request for UE %u, forwarding to AMF\n", req->gNB_CU_ue_id);
rrc_gNB_send_NGAP_UE_CONTEXT_RELEASE_REQ(instance,
@@ -1944,9 +2090,27 @@ static void rrc_CU_process_ue_context_modification_response(MessageDef *msg_p, i
rrc_gNB_generate_dedicatedRRCReconfiguration(rrc, UE);
}
+
+ // Reconfiguration should have been sent to the UE, so it will attempt the
+ // handover. In the F1 case, update with new RNTI, and update secondary UE
+ // association, so we can receive the new UE from the target DU (in N2/Xn,
+ // nothing is to be done, we wait for confirmation to release the UE in the
+ // CU/DU)
+ if (UE->ho_context && UE->ho_context->target && UE->ho_context->source) {
+ nr_ho_target_cu_t *target_ctx = UE->ho_context->target;
+ f1_ue_data_t ue_data = cu_get_f1_ue_data(UE->rrc_ue_id);
+ ue_data.secondary_ue = target_ctx->du_ue_id;
+ ue_data.du_assoc_id = target_ctx->du->assoc_id;
+ cu_remove_f1_ue_data(UE->rrc_ue_id);
+ cu_add_f1_ue_data(UE->rrc_ue_id, &ue_data);
+ LOG_I(NR_RRC, "UE %d handover: update RNTI from %04x to %04x\n", UE->rrc_ue_id, UE->rnti, target_ctx->new_rnti);
+ nr_ho_source_cu_t *source_ctx = UE->ho_context->source;
+ DevAssert(source_ctx->old_rnti == UE->rnti);
+ UE->rnti = target_ctx->new_rnti;
+ }
}
-static void rrc_CU_process_ue_modification_required(MessageDef *msg_p, instance_t instance)
+static void rrc_CU_process_ue_modification_required(MessageDef *msg_p, instance_t instance, sctp_assoc_t assoc_id)
{
gNB_RRC_INST *rrc = RC.nrrrc[instance];
f1ap_ue_context_modif_required_t *required = &F1AP_UE_CONTEXT_MODIFICATION_REQUIRED(msg_p);
@@ -1963,6 +2127,12 @@ static void rrc_CU_process_ue_modification_required(MessageDef *msg_p, instance_
return;
}
+ gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
+ if (UE->ho_context && UE->ho_context->source && UE->ho_context->source->du && UE->ho_context->source->du->assoc_id == assoc_id) {
+ LOG_W(NR_RRC, "UE %d: UE Context Modification Required during handover, ignoring message\n", UE->rrc_ue_id);
+ return;
+ }
+
if (required->du_to_cu_rrc_information && required->du_to_cu_rrc_information->cellGroupConfig) {
gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
LOG_I(RRC,
@@ -2010,7 +2180,7 @@ unsigned int mask_flip(unsigned int x) {
return((((x>>8) + (x<<8))&0xffff)>>6);
}
-static pdusession_level_qos_parameter_t *get_qos_characteristics(const int qfi, rrc_pdu_session_param_t *pduSession)
+pdusession_level_qos_parameter_t *get_qos_characteristics(const int qfi, rrc_pdu_session_param_t *pduSession)
{
pdusession_t *pdu = &pduSession->param;
for (int i = 0; i < pdu->nb_qos; i++) {
@@ -2022,7 +2192,7 @@ static pdusession_level_qos_parameter_t *get_qos_characteristics(const int qfi,
}
/* \bref return F1AP QoS characteristics based on Qos flow parameters */
-static f1ap_qos_characteristics_t get_qos_char_from_qos_flow_param(const pdusession_level_qos_parameter_t *qos_param)
+f1ap_qos_characteristics_t get_qos_char_from_qos_flow_param(const pdusession_level_qos_parameter_t *qos_param)
{
f1ap_qos_characteristics_t qos_char = {0};
if (qos_param->fiveQI_type == DYNAMIC) {
@@ -2181,6 +2351,24 @@ static void print_rrc_meas(FILE *f, const NR_MeasResults_t *measresults)
} else {
fprintf(f, "NOT PROVIDED\n");
}
+
+ if (measresults->measResultNeighCells
+ && measresults->measResultNeighCells->present == NR_MeasResults__measResultNeighCells_PR_measResultListNR) {
+ NR_MeasResultListNR_t *meas_neigh = measresults->measResultNeighCells->choice.measResultListNR;
+ for (int i = 0; i < meas_neigh->list.count; ++i) {
+ NR_MeasResultNR_t *measresultneigh = meas_neigh->list.array[i];
+ NR_MeasQuantityResults_t *neigh_mqr = measresultneigh->measResult.cellResults.resultsSSB_Cell;
+ fprintf(f, " neighboring cell for phyCellId %ld:\n resultSSB:", *measresultneigh->physCellId);
+ if (mqr != NULL) {
+ const long rrsrp = *neigh_mqr->rsrp - 156;
+ const float rrsrq = (float)(*neigh_mqr->rsrq - 87) / 2.0f;
+ const float rsinr = (float)(*neigh_mqr->sinr - 46) / 2.0f;
+ fprintf(f, "RSRP %ld dBm RSRQ %.1f dB SINR %.1f dB\n", rrsrp, rrsrq, rsinr);
+ } else {
+ fprintf(f, "NOT PROVIDED\n");
+ }
+ }
+ }
}
static const char *get_pdusession_status_text(pdu_session_status_t status)
@@ -2309,7 +2497,7 @@ void *rrc_gnb_task(void *args_p) {
/* From DU -> CU */
case F1AP_UL_RRC_MESSAGE:
rrc_gNB_decode_dcch(RC.nrrrc[instance], &F1AP_UL_RRC_MESSAGE(msg_p));
- free(F1AP_UL_RRC_MESSAGE(msg_p).rrc_container);
+ free_ul_rrc_message_transfer(&F1AP_UL_RRC_MESSAGE(msg_p));
break;
case NGAP_DOWNLINK_NAS:
@@ -2332,6 +2520,7 @@ void *rrc_gnb_task(void *args_p) {
case F1AP_SETUP_REQ:
AssertFatal(!NODE_IS_DU(RC.nrrrc[instance]->node_type), "should not receive F1AP_SETUP_REQUEST in DU!\n");
rrc_gNB_process_f1_setup_req(&F1AP_SETUP_REQ(msg_p), msg_p->ittiMsgHeader.originInstance);
+ free_f1ap_setup_request(&F1AP_SETUP_REQ(msg_p));
break;
case F1AP_UE_CONTEXT_SETUP_RESP:
@@ -2343,11 +2532,11 @@ void *rrc_gnb_task(void *args_p) {
break;
case F1AP_UE_CONTEXT_MODIFICATION_REQUIRED:
- rrc_CU_process_ue_modification_required(msg_p, instance);
+ rrc_CU_process_ue_modification_required(msg_p, instance, msg_p->ittiMsgHeader.originInstance);
break;
case F1AP_UE_CONTEXT_RELEASE_REQ:
- rrc_CU_process_ue_context_release_request(msg_p);
+ rrc_CU_process_ue_context_release_request(msg_p, msg_p->ittiMsgHeader.originInstance);
break;
case F1AP_UE_CONTEXT_RELEASE_COMPLETE:
@@ -2361,6 +2550,7 @@ void *rrc_gnb_task(void *args_p) {
case F1AP_GNB_DU_CONFIGURATION_UPDATE:
AssertFatal(!NODE_IS_DU(RC.nrrrc[instance]->node_type), "should not receive F1AP_SETUP_REQUEST in DU!\n");
rrc_gNB_process_f1_du_configuration_update(&F1AP_GNB_DU_CONFIGURATION_UPDATE(msg_p), msg_p->ittiMsgHeader.originInstance);
+ free_f1ap_du_configuration_update(&F1AP_GNB_DU_CONFIGURATION_UPDATE(msg_p));
break;
case F1AP_GNB_CU_CONFIGURATION_UPDATE_ACKNOWLEDGE:
diff --git a/openair2/RRC/NR/rrc_gNB_du.c b/openair2/RRC/NR/rrc_gNB_du.c
index e2806edf60..82b18f124b 100644
--- a/openair2/RRC/NR/rrc_gNB_du.c
+++ b/openair2/RRC/NR/rrc_gNB_du.c
@@ -24,6 +24,7 @@
#include "common/ran_context.h"
#include "nr_rrc_defs.h"
#include "rrc_gNB_UE_context.h"
+#include "rrc_gNB_mobility.h"
#include "openair2/F1AP/f1ap_common.h"
#include "openair2/F1AP/f1ap_ids.h"
#include "executables/softmodem-common.h"
@@ -383,8 +384,6 @@ void rrc_gNB_process_f1_setup_req(f1ap_setup_req_t *req, sctp_assoc_t assoc_id)
f1ap_setup_resp_t resp = {.transaction_id = req->transaction_id,
.num_cells_to_activate = 1,
.cells_to_activate[0] = cell};
- // free F1AP message after use
- free_f1ap_setup_request(req);
int num = read_version(TO_STRING(NR_RRC_VERSION), &resp.rrc_ver[0], &resp.rrc_ver[1], &resp.rrc_ver[2]);
AssertFatal(num == 3, "could not read RRC version string %s\n", TO_STRING(NR_RRC_VERSION));
if (rrc->node_name != NULL)
@@ -397,7 +396,12 @@ static int invalidate_du_connections(gNB_RRC_INST *rrc, sctp_assoc_t assoc_id)
int count = 0;
rrc_gNB_ue_context_t *ue_context_p = NULL;
RB_FOREACH(ue_context_p, rrc_nr_ue_tree_s, &rrc->rrc_ue_head) {
- uint32_t ue_id = ue_context_p->ue_context.rrc_ue_id;
+ gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
+ uint32_t ue_id = UE->rrc_ue_id;
+ if (UE->ho_context != NULL) {
+ LOG_W(NR_RRC, "DU disconnected while handover for UE %d active\n", ue_id);
+ nr_rrc_finalize_ho(UE);
+ }
f1_ue_data_t ue_data = cu_get_f1_ue_data(ue_id);
if (ue_data.du_assoc_id == assoc_id) {
/* this UE belongs to the DU that disconnected, set du_assoc_id to 0,
@@ -561,6 +565,18 @@ nr_rrc_du_container_t *get_du_by_assoc_id(gNB_RRC_INST *rrc, sctp_assoc_t assoc_
return RB_FIND(rrc_du_tree, &rrc->dus, &e);
}
+/* \brief find DU by cell ID. Note: currently the CU is limited to one cell per
+ * DU, hence here, DU == cell. Modify this to look up a specific cell. */
+nr_rrc_du_container_t *get_du_by_cell_id(gNB_RRC_INST *rrc, uint64_t cell_id)
+{
+ nr_rrc_du_container_t *du = NULL;
+ RB_FOREACH(du, rrc_du_tree, &rrc->dus) {
+ if (cell_id == du->setup_req->cell[0].info.nr_cellid)
+ return du;
+ }
+ return NULL;
+}
+
void dump_du_info(const gNB_RRC_INST *rrc, FILE *f)
{
fprintf(f, "%ld connected DUs \n", rrc->num_dus);
@@ -592,3 +608,31 @@ void dump_du_info(const gNB_RRC_INST *rrc, FILE *f)
}
}
}
+
+nr_rrc_du_container_t *find_target_du(gNB_RRC_INST *rrc, sctp_assoc_t source_assoc_id)
+{
+ nr_rrc_du_container_t *target_du = NULL;
+ nr_rrc_du_container_t *it = NULL;
+ bool next_du = false;
+ RB_FOREACH (it, rrc_du_tree, &rrc->dus) {
+ if (next_du == false && source_assoc_id != it->assoc_id) {
+ continue;
+ } else if (source_assoc_id == it->assoc_id) {
+ next_du = true;
+ } else {
+ target_du = it;
+ break;
+ }
+ }
+ if (target_du == NULL) {
+ RB_FOREACH (it, rrc_du_tree, &rrc->dus) {
+ if (source_assoc_id == it->assoc_id) {
+ continue;
+ } else {
+ target_du = it;
+ break;
+ }
+ }
+ }
+ return target_du;
+}
diff --git a/openair2/RRC/NR/rrc_gNB_du.h b/openair2/RRC/NR/rrc_gNB_du.h
index 3ee0b611ef..bd3c4f6443 100644
--- a/openair2/RRC/NR/rrc_gNB_du.h
+++ b/openair2/RRC/NR/rrc_gNB_du.h
@@ -41,6 +41,7 @@ void rrc_gNB_process_f1_du_configuration_update(struct f1ap_gnb_du_configuration
struct nr_rrc_du_container_t *get_du_for_ue(struct gNB_RRC_INST_s *rrc, uint32_t ue_id);
struct nr_rrc_du_container_t *get_du_by_assoc_id(struct gNB_RRC_INST_s *rrc, sctp_assoc_t assoc_id);
const struct f1ap_served_cell_info_t *get_cell_information_by_phycellId(int phyCellId);
+struct nr_rrc_du_container_t *get_du_by_cell_id(struct gNB_RRC_INST_s *rrc, uint64_t cell_id);
void dump_du_info(const struct gNB_RRC_INST_s *rrc, FILE *f);
@@ -48,4 +49,16 @@ int get_dl_band(const struct f1ap_served_cell_info_t *cell_info);
int get_ssb_scs(const struct f1ap_served_cell_info_t *cell_info);
int get_ssb_arfcn(const struct nr_rrc_du_container_t *du);
+struct nr_rrc_du_container_t *find_target_du(struct gNB_RRC_INST_s *rrc, sctp_assoc_t source_assoc_id);
+
+// the assoc_id might be 0 (if the DU goes offline). Below helper macro to
+// print an error and return from the function in that case
+#define RETURN_IF_INVALID_ASSOC_ID(assoc_id) \
+ { \
+ if (assoc_id == 0) { \
+ LOG_E(NR_RRC, "cannot send data: invalid assoc_id 0, DU offline\n"); \
+ return; \
+ } \
+ }
+
#endif /* RRC_GNB_DU_H_ */
diff --git a/openair2/RRC/NR/rrc_gNB_mobility.c b/openair2/RRC/NR/rrc_gNB_mobility.c
new file mode 100644
index 0000000000..137e7010c2
--- /dev/null
+++ b/openair2/RRC/NR/rrc_gNB_mobility.c
@@ -0,0 +1,360 @@
+/*
+ * 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
+
+#include "assertions.h"
+
+#include "rrc_gNB_mobility.h"
+
+#include "nr_rrc_proto.h"
+#include "rrc_gNB_du.h"
+#include "rrc_gNB_radio_bearers.h"
+#include "rrc_gNB_UE_context.h"
+#include "openair2/F1AP/f1ap_ids.h"
+#include "MESSAGES/asn1_msg.h"
+#include "nr_pdcp/nr_pdcp_oai_api.h"
+#include "openair3/SECU/key_nas_deriver.h"
+
+/* declared in rrc_gNB.c: should be cleaned up through helper function? useful? */
+extern mui_t rrc_gNB_mui;
+
+typedef enum { HO_CTX_BOTH, HO_CTX_SOURCE, HO_CTX_TARGET } ho_ctx_type_t;
+static nr_handover_context_t *alloc_ho_ctx(ho_ctx_type_t type)
+{
+ nr_handover_context_t *ho_ctx = calloc(1, sizeof(*ho_ctx));
+ AssertFatal(ho_ctx != NULL, "out of memory\n");
+ if (type == HO_CTX_SOURCE || type == HO_CTX_BOTH) {
+ ho_ctx->source = calloc(1, sizeof(*ho_ctx->source));
+ AssertFatal(ho_ctx->source != NULL, "out of memory\n");
+ }
+ if (type == HO_CTX_TARGET || type == HO_CTX_BOTH) {
+ ho_ctx->target = calloc(1, sizeof(*ho_ctx->target));
+ AssertFatal(ho_ctx->target != NULL, "out of memory\n");
+ }
+ return ho_ctx;
+}
+
+static void free_ho_ctx(nr_handover_context_t *ho_ctx)
+{
+ free(ho_ctx->source);
+ free(ho_ctx->target);
+ free(ho_ctx);
+}
+
+/* \brief Initiate a handover of UE to a specific target cell handled by this
+ * CU.
+ * \param ue a UE context for which the handover should be triggered. The UE
+ * context must be non-null (if the HO request comes from "outside" (N2, Xn),
+ * the UE contex must be created first.
+ * \param target_du the DU towards which to handover. Note: currently, the CU
+ * is limited to one cell per DU, so DU and cell are equivalent here.
+ * \param ho_ctxt contextual data for the type of handover (F1, N2, Xn) */
+static void nr_initiate_handover(const gNB_RRC_INST *rrc,
+ gNB_RRC_UE_t *ue,
+ const nr_rrc_du_container_t *source_du,
+ const nr_rrc_du_container_t *target_du,
+ uint8_t *ho_prep_buf,
+ uint32_t ho_prep_len,
+ ho_req_ack_t ack,
+ ho_success_t success,
+ ho_cancel_t cancel)
+{
+ DevAssert(rrc != NULL);
+ DevAssert(ue != NULL);
+ DevAssert(target_du != NULL);
+ // source_du might be NULL -> inter-CU handover
+ DevAssert(ho_prep_buf != NULL && ho_prep_len > 0);
+
+ if (ue->ho_context != NULL) {
+ LOG_E(NR_RRC, "handover for UE %u ongoing, cannot trigger new\n", ue->rrc_ue_id);
+ return;
+ }
+ // if any reconfiguration is ongoing, abort handover request
+ for (int i = 0; i < NR_RRC_TRANSACTION_IDENTIFIER_NUMBER; ++i) {
+ if (ue->xids[i] != RRC_ACTION_NONE) {
+ LOG_E(NR_RRC, "UE %d: ongoig transaction %d (action %d), cannot trigger handover\n", ue->rrc_ue_id, i, ue->xids[i]);
+ return;
+ }
+ }
+
+ // if we know the source DU (F1 handover), allocate the context for it,
+ // otherwise only for the target DU (N2, Xn)
+ ho_ctx_type_t ctx_type = source_du != NULL ? HO_CTX_BOTH : HO_CTX_TARGET;
+ nr_handover_context_t *ho_ctx = alloc_ho_ctx(ctx_type);
+ ho_ctx->target->du = target_du;
+ // we will know target->{du_ue_id,new_rnti} once we have UE ctxt setup
+ // response
+ ho_ctx->target->ho_req_ack = ack;
+ ho_ctx->target->ho_success = success;
+
+ const f1_ue_data_t ue_data = cu_get_f1_ue_data(ue->rrc_ue_id);
+ if (source_du != NULL) {
+ DevAssert(source_du->assoc_id == ue_data.du_assoc_id);
+ // we also have the source DU (F1 handover), store meta info
+ ho_ctx->source->ho_cancel = cancel;
+
+ ho_ctx->source->du = source_du;
+ ho_ctx->source->du_ue_id = ue_data.secondary_ue;
+ ho_ctx->source->old_rnti = ue->rnti;
+
+ int result = asn_copy(&asn_DEF_NR_CellGroupConfig, (void **)&ho_ctx->source->old_cellGroupConfig, ue->masterCellGroup);
+ AssertFatal(result == 0, "error during asn_copy() of CellGroupConfig\n");
+ }
+ ue->ho_context = ho_ctx;
+ LOG_A(NR_RRC,
+ "Handover triggered for UE %u/RNTI %04x towards DU %ld/assoc_id %d/PCI %d\n",
+ ue->rrc_ue_id,
+ ue->rnti,
+ target_du->setup_req->gNB_DU_id,
+ target_du->assoc_id,
+ target_du->setup_req->cell[0].info.nr_pci);
+
+ cu_to_du_rrc_information_t cu2du = {
+ .handoverPreparationInfo = ho_prep_buf,
+ .handoverPreparationInfo_length = ho_prep_len,
+ };
+
+ f1ap_drb_to_be_setup_t drbs[32] = {0}; // maximum DRB can be 32
+ int nb_drb = 0;
+ for (int i = 0; i < 32; ++i) { /* for each DRB */
+ drb_t *rrc_drb = &ue->established_drbs[i];
+ if (rrc_drb->status == DRB_INACTIVE)
+ continue;
+
+ f1ap_drb_to_be_setup_t *drb = &drbs[nb_drb];
+ nb_drb++;
+ drb->drb_id = rrc_drb->drb_id;
+ drb->rlc_mode = rrc->configuration.um_on_default_drb ? F1AP_RLC_MODE_UM_BIDIR : F1AP_RLC_MODE_AM;
+ memcpy(&drb->up_ul_tnl[0].tl_address, &rrc_drb->cuup_tunnel_config.addr.buffer, sizeof(uint8_t) * 4);
+ drb->up_ul_tnl[0].port = rrc->eth_params_s.my_portd;
+ drb->up_ul_tnl[0].teid = rrc_drb->cuup_tunnel_config.teid;
+ drb->up_ul_tnl_length = 1;
+
+ /* fetch an existing PDU session for this DRB */
+ rrc_pdu_session_param_t *pdu = find_pduSession_from_drbId(ue, drb->drb_id);
+ AssertFatal(pdu != NULL, "no PDU session for DRB ID %ld\n", drb->drb_id);
+ drb->nssai = pdu->param.nssai;
+
+ // for the moment, we only support one QoS flow. Put a reminder in case
+ // this changes
+ AssertFatal(pdu->param.nb_qos == 1, "only 1 Qos flow supported\n");
+ drb->drb_info.flows_to_be_setup_length = 1;
+ drb->drb_info.flows_mapped_to_drb = calloc(1, sizeof(f1ap_flows_mapped_to_drb_t));
+ AssertFatal(drb->drb_info.flows_mapped_to_drb, "could not allocate memory\n");
+ int qfi = rrc_drb->cnAssociation.sdap_config.mappedQoS_FlowsToAdd[0];
+ DevAssert(qfi > 0);
+ drb->drb_info.flows_mapped_to_drb[0].qfi = qfi;
+ pdusession_level_qos_parameter_t *in_qos_char = get_qos_characteristics(qfi, pdu);
+ drb->drb_info.flows_mapped_to_drb[0].qos_params.qos_characteristics = get_qos_char_from_qos_flow_param(in_qos_char);
+
+ /* the DRB QoS parameters: we just reuse the ones from the first flow */
+ drb->drb_info.drb_qos = drb->drb_info.flows_mapped_to_drb[0].qos_params;
+ }
+
+ f1ap_srb_to_be_setup_t srbs[2] = {{.srb_id = 1, .lcid = 1}, {.srb_id = 2, .lcid = 2}};
+ f1ap_served_cell_info_t *cell_info = &target_du->setup_req->cell[0].info;
+ RETURN_IF_INVALID_ASSOC_ID(target_du->assoc_id);
+ f1ap_ue_context_setup_t ue_context_setup_req = {
+ .gNB_CU_ue_id = ue->rrc_ue_id,
+ .gNB_DU_ue_id = 0, /* TODO: this should be optional! */
+ .plmn.mcc = cell_info->plmn.mcc,
+ .plmn.mnc = cell_info->plmn.mnc,
+ .plmn.mnc_digit_length = cell_info->plmn.mnc_digit_length,
+ .nr_cellid = cell_info->nr_cellid,
+ .servCellId = 0, // TODO: correct value?
+ .srbs_to_be_setup_length = 2,
+ .srbs_to_be_setup = srbs,
+ .drbs_to_be_setup_length = nb_drb,
+ .drbs_to_be_setup = drbs,
+ .cu_to_du_rrc_information = &cu2du,
+ };
+ rrc->mac_rrc.ue_context_setup_request(target_du->assoc_id, &ue_context_setup_req);
+}
+
+/*
+void nr_rrc_trigger_n2_ho(gNB_RRC_INST *rrc, int nr_cgi, uint8_t *ho_prep_info, uint32_t *ho_prep_len, void *pdu_session)
+{
+ // call from outside (via NGAP message), with the request for handover for a
+ // (any) UE. This handler should, in order:
+ //
+ // 1. look up the target DU =du= via get_du_by_cell_id(nr_cgi)
+ // (and reject the request if it can't find the DU)
+ // 2. create a context for a new UE =UE=, based on pdu_session information
+ // 3. call nr_initiate_handover(rrc, UE, NULL, du, ho_prep_info, ho_prep_len);
+ //
+ // after success of the DU, send the result via =success_ptr= to the source
+ // CU.
+}
+*/
+
+typedef struct deliver_ue_ctxt_modification_data_t {
+ gNB_RRC_INST *rrc;
+ f1ap_ue_context_modif_req_t *modification_req;
+ sctp_assoc_t assoc_id;
+} deliver_ue_ctxt_modification_data_t;
+static void rrc_deliver_ue_ctxt_modif_req(void *deliver_pdu_data, ue_id_t ue_id, int srb_id, char *buf, int size, int sdu_id)
+{
+ DevAssert(deliver_pdu_data != NULL);
+ deliver_ue_ctxt_modification_data_t *data = deliver_pdu_data;
+ data->modification_req->rrc_container = (uint8_t*)buf;
+ data->modification_req->rrc_container_length = size;
+ data->rrc->mac_rrc.ue_context_modification_request(data->assoc_id, data->modification_req);
+}
+static void rrc_gNB_trigger_reconfiguration_for_handover(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, uint8_t *rrc_reconf, int rrc_reconf_len)
+{
+ f1_ue_data_t ue_data = cu_get_f1_ue_data(ue->rrc_ue_id);
+
+ TransmActionInd_t transmission_action_indicator = TransmActionInd_STOP;
+ RETURN_IF_INVALID_ASSOC_ID(ue_data.du_assoc_id);
+ f1ap_ue_context_modif_req_t ue_context_modif_req = {
+ .gNB_CU_ue_id = ue->rrc_ue_id,
+ .gNB_DU_ue_id = ue_data.secondary_ue,
+ .plmn.mcc = rrc->configuration.mcc[0],
+ .plmn.mnc = rrc->configuration.mnc[0],
+ .plmn.mnc_digit_length = rrc->configuration.mnc_digit_length[0],
+ .nr_cellid = rrc->nr_cellid, // TODO target cell ID
+ .servCellId = 0, // TODO: correct value?
+ .ReconfigComplOutcome = RRCreconf_success,
+ .transm_action_ind = &transmission_action_indicator,
+ };
+ deliver_ue_ctxt_modification_data_t data = {.rrc = rrc,
+ .modification_req = &ue_context_modif_req,
+ .assoc_id = ue_data.du_assoc_id};
+ int srb_id = 1;
+ nr_pdcp_data_req_srb(ue->rrc_ue_id,
+ srb_id,
+ rrc_gNB_mui++,
+ rrc_reconf_len,
+ (unsigned char *const)rrc_reconf,
+ rrc_deliver_ue_ctxt_modif_req,
+ &data);
+}
+
+static void nr_rrc_f1_ho_acknowledge(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, uint8_t *rrc_reconf_buf, uint32_t rrc_reconf_len)
+{
+ // N2/Xn HO: fill with UE caps, as-context, rrc reconf, send to source CU
+ // also, fill the UE->rnti from the new one (in F1 case, happens after
+ // confirmation of ue ctxt modif
+
+ // F1 HO: handling of "source CU" information
+ DevAssert(UE->ho_context->source != NULL);
+ rrc_gNB_trigger_reconfiguration_for_handover(rrc, UE, rrc_reconf_buf, rrc_reconf_len);
+ LOG_A(NR_RRC, "HO acknowledged: Send reconfiguration for UE %u/RNTI %04x...\n", UE->rrc_ue_id, UE->rnti);
+
+ /* Re-establish SRB2 according to clause 5.3.5.6.3 of 3GPP TS 38.331
+ * (SRB1 is re-established with RRCReestablishment message)
+ */
+ int srb_id = 2;
+ if (UE->Srb[srb_id].Active) {
+ nr_pdcp_entity_security_keys_and_algos_t security_parameters;
+ /* Derive the keys from kgnb */
+ nr_derive_key(RRC_ENC_ALG, UE->ciphering_algorithm, UE->kgnb, security_parameters.ciphering_key);
+ nr_derive_key(RRC_INT_ALG, UE->integrity_algorithm, UE->kgnb, security_parameters.integrity_key);
+ security_parameters.integrity_algorithm = UE->integrity_algorithm;
+ security_parameters.ciphering_algorithm = UE->ciphering_algorithm;
+ nr_pdcp_reestablishment(UE->rrc_ue_id, srb_id, true, &security_parameters);
+ }
+}
+
+static void nr_rrc_f1_ho_complete(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE)
+{
+ DevAssert(UE->ho_context != NULL);
+ nr_ho_source_cu_t *source_ctx = UE->ho_context->source;
+ DevAssert(source_ctx != NULL);
+ RETURN_IF_INVALID_ASSOC_ID(source_ctx->du->assoc_id);
+ f1ap_ue_context_release_cmd_t cmd = {
+ .gNB_CU_ue_id = UE->rrc_ue_id,
+ .gNB_DU_ue_id = source_ctx->du_ue_id,
+ .cause = F1AP_CAUSE_RADIO_NETWORK, // better
+ .cause_value = 5, // 5 = F1AP_CauseRadioNetwork_interaction_with_other_procedure
+ .srb_id = DCCH,
+ };
+ rrc->mac_rrc.ue_context_release_command(source_ctx->du->assoc_id, &cmd);
+ LOG_I(NR_RRC, "UE %d Handover: trigger release on DU assoc_id %d\n", UE->rrc_ue_id, source_ctx->du->assoc_id);
+}
+
+static void nr_rrc_cancel_f1_ho(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE)
+{
+ DevAssert(UE->ho_context != NULL);
+ nr_ho_target_cu_t *target_ctx = UE->ho_context->target;
+ DevAssert(target_ctx != NULL);
+ f1ap_ue_context_release_cmd_t cmd = {
+ .gNB_CU_ue_id = UE->rrc_ue_id,
+ .gNB_DU_ue_id = target_ctx->du_ue_id,
+ .cause = F1AP_CAUSE_RADIO_NETWORK, // better
+ .cause_value = 5, // 5 = F1AP_CauseRadioNetwork_interaction_with_other_procedure
+ .srb_id = DCCH,
+ };
+ rrc->mac_rrc.ue_context_release_command(target_ctx->du->assoc_id, &cmd);
+}
+
+void nr_rrc_trigger_f1_ho(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, nr_rrc_du_container_t *source_du, nr_rrc_du_container_t *target_du)
+{
+ DevAssert(rrc != NULL);
+ DevAssert(ue != NULL);
+
+ uint8_t buf[NR_RRC_BUF_SIZE];
+ int size = do_NR_HandoverPreparationInformation(ue->ue_cap_buffer.buf, ue->ue_cap_buffer.len, buf, sizeof buf);
+
+ // corresponds to a "handover request", 38.300 Sec 9.3.2.3
+ // see also 38.413 Sec 9.3.1.29 for information on source-CU to target-CU
+ // information (Source NG-RAN Node to Target NG-RAN Node Transparent Container)
+ // here: target Cell is preselected, target CU has access to UE information
+ // and therefore also the PDU sessions. Orig RRC reconfiguration should be in
+ // handover preparation information
+ ho_req_ack_t ack = nr_rrc_f1_ho_acknowledge;
+ ho_success_t success = nr_rrc_f1_ho_complete;
+ ho_cancel_t cancel = nr_rrc_cancel_f1_ho;
+ nr_initiate_handover(rrc, ue, source_du, target_du, buf, size, ack, success, cancel);
+}
+
+void nr_rrc_finalize_ho(gNB_RRC_UE_t *ue)
+{
+ if (ue->ho_context->source)
+ ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, ue->ho_context->source->old_cellGroupConfig);
+ free_ho_ctx(ue->ho_context);
+ ue->ho_context = NULL;
+}
+
+void nr_HO_F1_trigger_telnet(gNB_RRC_INST *rrc, uint32_t rrc_ue_id)
+{
+ rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(rrc, rrc_ue_id);
+ if (ue_context_p == NULL) {
+ LOG_E(NR_RRC, "cannot find UE context for UE ID %d\n", rrc_ue_id);
+ return;
+ }
+ gNB_RRC_UE_t *ue = &ue_context_p->ue_context;
+ nr_rrc_du_container_t *source_du = get_du_for_ue(rrc, ue->rrc_ue_id);
+ if (source_du == NULL) {
+ f1_ue_data_t ue_data = cu_get_f1_ue_data(rrc_ue_id);
+ LOG_E(NR_RRC, "cannot get source gNB-DU with assoc_id %d for UE %u\n", ue_data.du_assoc_id, ue->rrc_ue_id);
+ return;
+ }
+
+ nr_rrc_du_container_t *target_du = find_target_du(rrc, source_du->assoc_id);
+ if (target_du == NULL) {
+ LOG_E(NR_RRC, "No target gNB-DU found. Handover for UE %u aborted.\n", ue->rrc_ue_id);
+ return;
+ }
+
+ nr_rrc_trigger_f1_ho(rrc, ue, source_du, target_du);
+}
diff --git a/openair2/RRC/NR/rrc_gNB_mobility.h b/openair2/RRC/NR/rrc_gNB_mobility.h
new file mode 100644
index 0000000000..5046176c1d
--- /dev/null
+++ b/openair2/RRC/NR/rrc_gNB_mobility.h
@@ -0,0 +1,77 @@
+/* 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 RRC_GNB_MOBILITY_H_
+#define RRC_GNB_MOBILITY_H_
+
+#include
+
+/* forward declarations */
+typedef struct gNB_RRC_INST_s gNB_RRC_INST;
+typedef struct gNB_RRC_UE_s gNB_RRC_UE_t;
+typedef struct nr_rrc_du_container_t nr_rrc_du_container_t;
+
+typedef struct NR_CellGroupConfig NR_CellGroupConfig_t;
+
+typedef void (*ho_cancel_t)(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue);
+typedef struct nr_ho_source_cu {
+ /// pointer to the (source) DU structure
+ const nr_rrc_du_container_t *du;
+ /// (source) DU UE ID; in F1, the DU UE ID will change to the new (target) DU
+ /// UE ID during handover, and the CU needs to keep track of this in case of
+ /// reestablishment
+ uint32_t du_ue_id;
+ /// old (source) RNTI (to recognize a UE during reestablishment)
+ rnti_t old_rnti;
+ /// old (source) CellGroupConfig (to send the cellGroupConfig in case of
+ /// reestablishment)
+ NR_CellGroupConfig_t *old_cellGroupConfig;
+ /// function pointer to announce the handover cancellation, e.g.,
+ /// reestablishment
+ ho_cancel_t ho_cancel;
+} nr_ho_source_cu_t;
+
+/* acknowledgement of handover request. buf+len is the RRC Reconfiguration */
+typedef void (*ho_req_ack_t)(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, uint8_t *buf, uint32_t len);
+typedef void (*ho_success_t)(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue);
+typedef struct nr_ho_target_cu {
+ /// pointer to the (target) DU structure
+ const nr_rrc_du_container_t *du;
+ /// (target) DU UE ID; the (source) DU UE ID will change to the new (target)
+ /// DU UE ID after sending a reconfiguration, which is later than when
+ /// receiving this ID
+ uint32_t du_ue_id;
+ /// new (target) RNTI (as for du_ue_id)
+ rnti_t new_rnti;
+ /// function pointer to announce handover request acknowledgment
+ ho_req_ack_t ho_req_ack;
+ /// function pointer to announce handover success
+ ho_success_t ho_success;
+} nr_ho_target_cu_t;
+
+typedef struct nr_handover_context_s {
+ nr_ho_source_cu_t *source;
+ nr_ho_target_cu_t *target;
+} nr_handover_context_t;
+
+void nr_rrc_trigger_f1_ho(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, nr_rrc_du_container_t *source_du, nr_rrc_du_container_t *target_du);
+void nr_rrc_finalize_ho(gNB_RRC_UE_t *ue);
+
+#endif /* RRC_GNB_MOBILITY_H_ */
diff --git a/openair2/RRC/NR/rrc_gNB_radio_bearers.c b/openair2/RRC/NR/rrc_gNB_radio_bearers.c
index ab2bf13071..aec7865a1c 100644
--- a/openair2/RRC/NR/rrc_gNB_radio_bearers.c
+++ b/openair2/RRC/NR/rrc_gNB_radio_bearers.c
@@ -181,6 +181,15 @@ uint8_t get_next_available_drb_id(gNB_RRC_UE_t *ue)
return DRB_INACTIVE;
}
+int get_number_active_drbs(gNB_RRC_UE_t *ue)
+{
+ int n = 0;
+ for (int i = 0; i < MAX_DRBS_PER_UE; ++i)
+ if (ue->established_drbs[i].status != DRB_INACTIVE)
+ n++;
+ return n;
+}
+
bool drb_is_active(gNB_RRC_UE_t *ue, uint8_t drb_id)
{
drb_t *drb = get_drb(ue, drb_id);
diff --git a/openair2/RRC/NR/rrc_gNB_radio_bearers.h b/openair2/RRC/NR/rrc_gNB_radio_bearers.h
index 82502d8669..29fead6bea 100644
--- a/openair2/RRC/NR/rrc_gNB_radio_bearers.h
+++ b/openair2/RRC/NR/rrc_gNB_radio_bearers.h
@@ -61,6 +61,9 @@ drb_t *generateDRB(gNB_RRC_UE_t *ue,
/// @brief return the next available (inactive) DRB ID of UE ue
uint8_t get_next_available_drb_id(gNB_RRC_UE_t *ue);
+/// @brief returns the number of active DRBs for this UE
+int get_number_active_drbs(gNB_RRC_UE_t *ue);
+
/// @brief check if DRB with ID drb_id of UE ue is active
bool drb_is_active(gNB_RRC_UE_t *ue, uint8_t drb_id);
diff --git a/openair2/RRC/NR_UE/rrc_UE.c b/openair2/RRC/NR_UE/rrc_UE.c
index 7ea7dad968..89d73f1abc 100644
--- a/openair2/RRC/NR_UE/rrc_UE.c
+++ b/openair2/RRC/NR_UE/rrc_UE.c
@@ -68,7 +68,7 @@
#include "SIMULATION/TOOLS/sim.h" // for taus
-#include "nr_nas_msg_sim.h"
+#include "nr_nas_msg.h"
#include "openair2/SDAP/nr_sdap/nr_sdap_entity.h"
static NR_UE_RRC_INST_t *NR_UE_rrc_inst;
diff --git a/openair2/UTIL/OMG/omg.c b/openair2/UTIL/OMG/omg.c
index 23b8bb114c..67a78d245a 100644
--- a/openair2/UTIL/OMG/omg.c
+++ b/openair2/UTIL/OMG/omg.c
@@ -423,7 +423,7 @@ usage (void) {
int
get_options (int argc, char *argv[]) {
int node_t;
- char tag;
+ int tag;
while ((tag =
getopt (argc, argv,
diff --git a/openair3/NAS/COMMON/ESM/MSG/PduSessionEstablishmentAccept.c b/openair3/NAS/COMMON/ESM/MSG/PduSessionEstablishmentAccept.c
index f5d053ae58..2872ba3d5e 100644
--- a/openair3/NAS/COMMON/ESM/MSG/PduSessionEstablishmentAccept.c
+++ b/openair3/NAS/COMMON/ESM/MSG/PduSessionEstablishmentAccept.c
@@ -21,7 +21,7 @@
#include "PduSessionEstablishmentAccept.h"
#include "common/utils/LOG/log.h"
-#include "nr_nas_msg_sim.h"
+#include "nr_nas_msg.h"
#include "common/utils/tun_if.h"
#include "openair2/SDAP/nr_sdap/nr_sdap.h"
@@ -124,18 +124,28 @@ void capture_pdu_session_establishment_accept_msg(uint8_t *buffer, uint32_t msg_
case IEI_PDU_ADDRESS:
LOG_T(NAS, "PDU SESSION ESTABLISHMENT ACCEPT - Received PDU Address IE\n");
psea_msg.pdu_addr_ie.pdu_length = *curPtr++;
- psea_msg.pdu_addr_ie.pdu_type = *curPtr++;
+ /* Octet 3 */
+ // PDU type (3 bits)
+ psea_msg.pdu_addr_ie.pdu_type = *curPtr & 0x07;
+ // SMF's IPv6 link local address (1 bit)
+ uint8_t si6lla = (*curPtr >> 3) & 0x01;
+ if (si6lla)
+ LOG_E(NAS, "SMF's IPv6 link local address is not handled\n");
+ curPtr++;
+
+ /* Octet 4 to n */
+ // PDU address information
uint8_t *addr = psea_msg.pdu_addr_ie.pdu_addr_oct;
if (psea_msg.pdu_addr_ie.pdu_type == PDU_SESSION_TYPE_IPV4) {
- for (int i = 0; i < 4; ++i)
+ for (int i = 0; i < IPv4_ADDRESS_LENGTH; ++i)
addr[i] = *curPtr++;
char ip[20];
capture_ipv4_addr(&addr[0], ip, sizeof(ip));
tun_config(1, ip, NULL, "oaitun_ue");
setup_ue_ipv4_route(1, ip, "oaitun_ue");
} else if (psea_msg.pdu_addr_ie.pdu_type == PDU_SESSION_TYPE_IPV6) {
- for (int i = 0; i < 8; ++i)
+ for (int i = 0; i < IPv6_INTERFACE_ID_LENGTH; ++i)
addr[i] = *curPtr++;
char ipv6[40];
capture_ipv6_addr(addr, ipv6, sizeof(ipv6));
@@ -145,12 +155,12 @@ void capture_pdu_session_establishment_accept_msg(uint8_t *buffer, uint32_t msg_
// IPv4v6, the PDU address information in octet 4 to octet 11
// contains an interface identifier for the IPv6 link local address
// and in octet 12 to octet 15 contains an IPv4 address."
- for (int i = 0; i < 12; ++i)
+ for (int i = 0; i < IPv4_ADDRESS_LENGTH + IPv6_INTERFACE_ID_LENGTH; ++i)
addr[i] = *curPtr++;
char ipv6[40];
capture_ipv6_addr(addr, ipv6, sizeof(ipv6));
char ipv4[20];
- capture_ipv4_addr(&addr[8], ipv4, sizeof(ipv4));
+ capture_ipv4_addr(&addr[IPv6_INTERFACE_ID_LENGTH], ipv4, sizeof(ipv4));
tun_config(1, ipv4, ipv6, "oaitun_ue");
setup_ue_ipv4_route(1, ipv4, "oaitun_ue");
} else {
@@ -164,42 +174,47 @@ void capture_pdu_session_establishment_accept_msg(uint8_t *buffer, uint32_t msg_
curPtr++; /* TS 24.008 10.5.7.3 */
break;
- case IEI_SNSSAI: /* Ommited */
+ case IEI_SNSSAI: {
LOG_T(NAS, "PDU SESSION ESTABLISHMENT ACCEPT - Received S-NSSAI IE\n");
uint8_t snssai_length = *curPtr;
curPtr += (snssai_length + sizeof(snssai_length));
break;
+ }
case IEI_ALWAYSON_PDU: /* Ommited */
LOG_T(NAS, "PDU SESSION ESTABLISHMENT ACCEPT - Received Always-on PDU Session indication IE\n");
curPtr++;
break;
- case IEI_MAPPED_EPS: /* Ommited */
+ case IEI_MAPPED_EPS: {
LOG_T(NAS, "PDU SESSION ESTABLISHMENT ACCEPT - Received Mapped EPS bearer context IE\n");
uint16_t mapped_eps_length = getShort(curPtr);
curPtr += mapped_eps_length;
break;
+ }
- case IEI_EAP_MSG: /* Ommited */
+ case IEI_EAP_MSG: {
LOG_T(NAS, "PDU SESSION ESTABLISHMENT ACCEPT - Received EAP message IE\n");
uint16_t eap_length = getShort(curPtr);
curPtr += (eap_length + sizeof(eap_length));
break;
+ }
- case IEI_AUTH_QOS_DESC: /* Ommited */
+ case IEI_AUTH_QOS_DESC: {
LOG_T(NAS, "PDU SESSION ESTABLISHMENT ACCEPT - Received Authorized QoS flow descriptions IE\n");
psea_msg.qos_fd_ie.length = getShort(curPtr);
curPtr += (psea_msg.qos_fd_ie.length + sizeof(psea_msg.qos_fd_ie.length));
break;
+ }
- case IEI_EXT_CONF_OPT: /* Ommited */
+ case IEI_EXT_CONF_OPT: {
LOG_T(NAS, "PDU SESSION ESTABLISHMENT ACCEPT - Received Extended protocol configuration options IE\n");
psea_msg.ext_pp_ie.length = getShort(curPtr);
curPtr += (psea_msg.ext_pp_ie.length + sizeof(psea_msg.ext_pp_ie.length));
break;
+ }
- case IEI_DNN:
+ case IEI_DNN: {
LOG_T(NAS, "PDU SESSION ESTABLISHMENT ACCEPT - Received DNN IE\n");
psea_msg.dnn_ie.dnn_length = *curPtr++;
char apn[APN_MAX_LEN];
@@ -213,6 +228,7 @@ void capture_pdu_session_establishment_accept_msg(uint8_t *buffer, uint32_t msg_
curPtr = buffer + msg_length; // we force stop processing
break;
+ }
default:
LOG_T(NAS, "PDU SESSION ESTABLISHMENT ACCEPT - Not known IE\n");
diff --git a/openair3/NAS/COMMON/ESM/MSG/PduSessionEstablishmentAccept.h b/openair3/NAS/COMMON/ESM/MSG/PduSessionEstablishmentAccept.h
index a254debfbe..c283079998 100644
--- a/openair3/NAS/COMMON/ESM/MSG/PduSessionEstablishmentAccept.h
+++ b/openair3/NAS/COMMON/ESM/MSG/PduSessionEstablishmentAccept.h
@@ -42,6 +42,9 @@
#define PDU_SESSION_TYPE_IPV4 0b001
#define PDU_SESSION_TYPE_IPV6 0b010
#define PDU_SESSION_TYPE_IPV4V6 0b011
+#define IPv4_ADDRESS_LENGTH 4 // length of the IPv4 address associated with a PDU session
+#define IPv6_INTERFACE_ID_LENGTH 8 // interface identifier for the IPv6 link local address
+#define SMF_IPv6_LLA_LENGTH 16
/* Rule operation codes - TS 24.501 Table 9.11.4.13.1 */
@@ -114,7 +117,8 @@ typedef struct pdu_address_s {
uint8_t pdu_iei; /* PDU Address IEI (0x29) */
uint8_t pdu_length; /* Length of PDU address contents */
uint8_t pdu_type; /* PDU session type value */
- uint8_t pdu_addr_oct[12]; /* PDU address information (depending on type, up to 12 bytes)*/
+ /// PDU address information (depending on type, up to 12 bytes)
+ uint8_t pdu_addr_oct[IPv4_ADDRESS_LENGTH + IPv6_INTERFACE_ID_LENGTH];
} pdu_address_t; /* TS 24.501 9.11.4.10 */
typedef struct dnn_s {
diff --git a/openair3/NAS/COMMON/NR_NAS_defs.h b/openair3/NAS/COMMON/NR_NAS_defs.h
index b6c0b715fc..f49852f62c 100644
--- a/openair3/NAS/COMMON/NR_NAS_defs.h
+++ b/openair3/NAS/COMMON/NR_NAS_defs.h
@@ -63,55 +63,55 @@ typedef enum {
SGSmobilitymanagementmessages=0x7e,
} Extendedprotocoldiscriminator_t;
-#define FOREACH_TYPE(TYPE_DEF) \
- TYPE_DEF( Registrationrequest,0x41 )\
-TYPE_DEF( Registrationaccept,0x42 )\
-TYPE_DEF( Registrationcomplete,0x43 )\
-TYPE_DEF( Registrationreject,0x44 )\
-TYPE_DEF( DeregistrationrequestUEoriginating,0x45 )\
-TYPE_DEF( DeregistrationacceptUEoriginating,0x46 )\
-TYPE_DEF( DeregistrationrequestUEterminated,0x47 )\
-TYPE_DEF( DeregistrationacceptUEterminated,0x48 )\
-TYPE_DEF( Servicerequest,0x4c )\
-TYPE_DEF( Servicereject,0x4d )\
-TYPE_DEF( Serviceaccept,0x4e )\
-TYPE_DEF( Controlplaneservicerequest,0x4f )\
-TYPE_DEF( Networkslicespecificauthenticationcommand,0x50 )\
-TYPE_DEF( Networkslicespecificauthenticationcomplete,0x51 )\
-TYPE_DEF( Networkslicespecificauthenticationresult,0x52 )\
-TYPE_DEF( Configurationupdatecommand,0x54 )\
-TYPE_DEF( Configurationupdatecomplete,0x55 )\
-TYPE_DEF( Authenticationrequest,0x56 )\
-TYPE_DEF( Authenticationresponse,0x57 )\
-TYPE_DEF( Authenticationreject,0x58 )\
-TYPE_DEF( Authenticationfailure,0x59 )\
-TYPE_DEF( Authenticationresult,0x5a )\
-TYPE_DEF( Identityrequest,0x5b )\
-TYPE_DEF( Identityresponse,0x5c )\
-TYPE_DEF( Securitymodecommand,0x5d )\
-TYPE_DEF( Securitymodecomplete,0x5e )\
-TYPE_DEF( Securitymodereject,0x5f )\
-TYPE_DEF( SGMMstatus,0x64 )\
-TYPE_DEF( Notification,0x65 )\
-TYPE_DEF( Notificationresponse,0x66 )\
-TYPE_DEF( ULNAStransport,0x67 )\
-TYPE_DEF( DLNAStransport,0x68 )\
-TYPE_DEF( PDUsessionestablishmentrequest,0xc1 )\
-TYPE_DEF( PDUsessionestablishmentaccept,0xc2 )\
-TYPE_DEF( PDUsessionestablishmentreject,0xc3 )\
-TYPE_DEF( PDUsessionauthenticationcommand,0xc5 )\
-TYPE_DEF( PDUsessionauthenticationcomplete,0xc6 )\
-TYPE_DEF( PDUsessionauthenticationresult,0xc7 )\
-TYPE_DEF( PDUsessionmodificationrequest,0xc9 )\
-TYPE_DEF( PDUsessionmodificationreject,0xca )\
-TYPE_DEF( PDUsessionmodificationcommand,0xcb )\
-TYPE_DEF( PDUsessionmodificationcomplete,0xcc )\
-TYPE_DEF( PDUsessionmodificationcommandreject,0xcd )\
-TYPE_DEF( PDUsessionreleaserequest,0xd1 )\
-TYPE_DEF( PDUsessionreleasereject,0xd2 )\
-TYPE_DEF( PDUsessionreleasecommand,0xd3 )\
-TYPE_DEF( PDUsessionreleasecomplete,0xd4 )\
-TYPE_DEF( SGSMstatus,0xd6 )\
+#define FOREACH_TYPE(TYPE_DEF) \
+ TYPE_DEF(REGISTRATION_REQUEST, 0x41) \
+ TYPE_DEF(REGISTRATION_ACCEPT, 0x42) \
+ TYPE_DEF(REGISTRATION_COMPLETE, 0x43) \
+ TYPE_DEF(REGISTRATION_REJECT, 0x44) \
+ TYPE_DEF(FGS_DEREGISTRATION_REQUEST_UE_ORIGINATING, 0x45) \
+ TYPE_DEF(FGS_DEREGISTRATION_ACCEPT_UE_ORIGINATING, 0x46) \
+ TYPE_DEF(DeregistrationrequestUEterminated, 0x47) \
+ TYPE_DEF(DeregistrationacceptUEterminated, 0x48) \
+ TYPE_DEF(Servicerequest, 0x4c) \
+ TYPE_DEF(Servicereject, 0x4d) \
+ TYPE_DEF(Serviceaccept, 0x4e) \
+ TYPE_DEF(Controlplaneservicerequest, 0x4f) \
+ TYPE_DEF(Networkslicespecificauthenticationcommand, 0x50) \
+ TYPE_DEF(Networkslicespecificauthenticationcomplete, 0x51) \
+ TYPE_DEF(Networkslicespecificauthenticationresult, 0x52) \
+ TYPE_DEF(Configurationupdatecommand, 0x54) \
+ TYPE_DEF(Configurationupdatecomplete, 0x55) \
+ TYPE_DEF(FGS_AUTHENTICATION_REQUEST, 0x56) \
+ TYPE_DEF(FGS_AUTHENTICATION_RESPONSE, 0x57) \
+ TYPE_DEF(Authenticationreject, 0x58) \
+ TYPE_DEF(Authenticationfailure, 0x59) \
+ TYPE_DEF(Authenticationresult, 0x5a) \
+ TYPE_DEF(FGS_IDENTITY_REQUEST, 0x5b) \
+ TYPE_DEF(FGS_IDENTITY_RESPONSE, 0x5c) \
+ TYPE_DEF(FGS_SECURITY_MODE_COMMAND, 0x5d) \
+ TYPE_DEF(FGS_SECURITY_MODE_COMPLETE, 0x5e) \
+ TYPE_DEF(Securitymodereject, 0x5f) \
+ TYPE_DEF(SGMMstatus, 0x64) \
+ TYPE_DEF(Notification, 0x65) \
+ TYPE_DEF(Notificationresponse, 0x66) \
+ TYPE_DEF(FGS_UPLINK_NAS_TRANSPORT, 0x67) \
+ TYPE_DEF(FGS_DOWNLINK_NAS_TRANSPORT, 0x68) \
+ TYPE_DEF(FGS_PDU_SESSION_ESTABLISHMENT_REQ, 0xc1) \
+ TYPE_DEF(FGS_PDU_SESSION_ESTABLISHMENT_ACC, 0xc2) \
+ TYPE_DEF(FGS_PDU_SESSION_ESTABLISHMENT_REJ, 0xc3) \
+ TYPE_DEF(PDUsessionauthenticationcommand, 0xc5) \
+ TYPE_DEF(PDUsessionauthenticationcomplete, 0xc6) \
+ TYPE_DEF(PDUsessionauthenticationresult, 0xc7) \
+ TYPE_DEF(PDUsessionmodificationrequest, 0xc9) \
+ TYPE_DEF(PDUsessionmodificationreject, 0xca) \
+ TYPE_DEF(PDUsessionmodificationcommand, 0xcb) \
+ TYPE_DEF(PDUsessionmodificationcomplete, 0xcc) \
+ TYPE_DEF(PDUsessionmodificationcommandreject, 0xcd) \
+ TYPE_DEF(PDUsessionreleaserequest, 0xd1) \
+ TYPE_DEF(PDUsessionreleasereject, 0xd2) \
+ TYPE_DEF(PDUsessionreleasecommand, 0xd3) \
+ TYPE_DEF(PDUsessionreleasecomplete, 0xd4) \
+ TYPE_DEF(SGSMstatus, 0xd6)
static const text_info_t message_text_info[] = {
FOREACH_TYPE(TO_TEXT)
@@ -124,11 +124,11 @@ typedef enum {
// TS 24.501
typedef enum {
- notsecurityprotected=0,
- Integrityprotected=1,
- Integrityprotectedandciphered=2,
- Integrityprotectedwithnew5GNASsecuritycontext=3,
- Integrityprotectedandcipheredwithnew5GNASsecuritycontext=4,
+ PLAIN_5GS_MSG = 0,
+ INTEGRITY_PROTECTED = 1,
+ INTEGRITY_PROTECTED_AND_CIPHERED = 2,
+ INTEGRITY_PROTECTED_WITH_NEW_SECU_CTX = 3,
+ INTEGRITY_PROTECTED_AND_CIPHERED_WITH_NEW_SECU_CTX = 4,
} Security_header_t;
typedef enum {
diff --git a/openair3/NAS/NR_UE/nr_nas_msg_sim.c b/openair3/NAS/NR_UE/nr_nas_msg.c
similarity index 96%
rename from openair3/NAS/NR_UE/nr_nas_msg_sim.c
rename to openair3/NAS/NR_UE/nr_nas_msg.c
index c942e079cd..8d07ea8455 100644
--- a/openair3/NAS/NR_UE/nr_nas_msg_sim.c
+++ b/openair3/NAS/NR_UE/nr_nas_msg.c
@@ -19,7 +19,7 @@
* contact@openairinterface.org
*/
-/*! \file nr_nas_msg_sim.c
+/*! \file nr_nas_msg.c
* \brief simulator for nr nas message
* \author Yoshio INOUE, Masayuki HARADA
* \email yoshio.inoue@fujitsu.com,masayuki.harada@fujitsu.com
@@ -35,7 +35,7 @@
#include "nas_log.h"
#include "TLVDecoder.h"
#include "TLVEncoder.h"
-#include "nr_nas_msg_sim.h"
+#include "nr_nas_msg.h"
#include "aka_functions.h"
#include "secu_defs.h"
#include "kdf.h"
@@ -848,16 +848,15 @@ static void generateRegistrationComplete(nr_ue_nas_t *nas,
}
}
-void decodeDownlinkNASTransport(as_nas_info_t *initialNasMsg, uint8_t *pdu_buffer)
+/**
+ * @brief Handle DL NAS Transport and process piggybacked 5GSM messages
+ */
+static void handleDownlinkNASTransport(uint8_t *pdu_buffer, uint32_t msg_length)
{
uint8_t msg_type = *(pdu_buffer + 16);
if (msg_type == FGS_PDU_SESSION_ESTABLISHMENT_ACC) {
- uint8_t *ip_p = pdu_buffer + 39;
- char ip[20];
- sprintf(ip, "%d.%d.%d.%d", *(ip_p), *(ip_p + 1), *(ip_p + 2), *(ip_p + 3));
- LOG_A(NAS, "Received PDU Session Establishment Accept\n");
- tun_config(1, ip, NULL, "oaitun_ue");
- setup_ue_ipv4_route(1, ip, "oaitun_ue");
+ LOG_A(NAS, "Received PDU Session Establishment Accept in DL NAS Transport\n");
+ capture_pdu_session_establishment_accept_msg(pdu_buffer, msg_length);
} else {
LOG_E(NAS, "Received unexpected message in DLinformationTransfer %d\n", msg_type);
}
@@ -1393,7 +1392,7 @@ void *nas_nrue(void *args_p)
if (security_state == NAS_SECURITY_UNPROTECTED) {
int msg_type = get_msg_type(pdu_buffer, pdu_length);
/* for the moment, only FGS_DEREGISTRATION_ACCEPT is accepted */
- if (msg_type == FGS_DEREGISTRATION_ACCEPT)
+ if (msg_type == FGS_DEREGISTRATION_ACCEPT_UE_ORIGINATING)
security_state = NAS_SECURITY_INTEGRITY_PASSED;
}
@@ -1415,43 +1414,24 @@ void *nas_nrue(void *args_p)
handle_security_mode_command(nas, &initialNasMsg, pdu_buffer, pdu_length);
break;
case FGS_DOWNLINK_NAS_TRANSPORT:
- decodeDownlinkNASTransport(&initialNasMsg, pdu_buffer);
+ handleDownlinkNASTransport(pdu_buffer, pdu_length);
break;
case REGISTRATION_ACCEPT:
handle_registration_accept(nas, pdu_buffer, pdu_length);
break;
- case FGS_DEREGISTRATION_ACCEPT:
+ case FGS_DEREGISTRATION_ACCEPT_UE_ORIGINATING:
LOG_I(NAS, "received deregistration accept\n");
break;
- case FGS_PDU_SESSION_ESTABLISHMENT_ACC: {
- uint8_t offset = 0;
- uint8_t *payload_container = pdu_buffer;
- offset += SECURITY_PROTECTED_5GS_NAS_MESSAGE_HEADER_LENGTH;
- uint32_t payload_container_length = htons(((dl_nas_transport_t *)(pdu_buffer + offset))->payload_container_length);
- if ((payload_container_length >= PAYLOAD_CONTAINER_LENGTH_MIN)
- && (payload_container_length <= PAYLOAD_CONTAINER_LENGTH_MAX))
- offset += (PLAIN_5GS_NAS_MESSAGE_HEADER_LENGTH + 3);
- if (offset < NAS_CONN_ESTABLI_CNF(msg_p).nasMsg.length)
- payload_container = pdu_buffer + offset;
-
- while (offset < payload_container_length) {
- if (*(payload_container + offset) == 0x29) { // PDU address IEI
- if ((*(payload_container + offset + 1) == 0x05) && (*(payload_container + offset + 2) == 0x01)) { // IPV4
- uint8_t *ip_p = payload_container + offset + 3;
- char ip[20];
- snprintf(ip, sizeof(ip), "%d.%d.%d.%d", *(ip_p), *(ip_p + 1), *(ip_p + 2), *(ip_p + 3));
- LOG_I(NAS, "Received PDU Session Establishment Accept, UE IP: %s\n", ip);
- tun_config(1, ip, NULL, "oaitun_ue");
- setup_ue_ipv4_route(1, ip, "oaitun_ue");
- break;
- }
- }
- offset++;
- }
- } break;
+ case FGS_PDU_SESSION_ESTABLISHMENT_ACC:
+ capture_pdu_session_establishment_accept_msg(pdu_buffer, pdu_length);
+ break;
case FGS_PDU_SESSION_ESTABLISHMENT_REJ:
LOG_E(NAS, "Received PDU Session Establishment reject\n");
break;
+ case REGISTRATION_REJECT:
+ LOG_E(NAS, "Received Registration reject cause: %s\n", cause_text_info[pdu_buffer[17]].text);
+ exit(1);
+ break;
default:
LOG_W(NR_RRC, "unknown message type %d\n", msg_type);
break;
diff --git a/openair3/NAS/NR_UE/nr_nas_msg_sim.h b/openair3/NAS/NR_UE/nr_nas_msg.h
similarity index 53%
rename from openair3/NAS/NR_UE/nr_nas_msg_sim.h
rename to openair3/NAS/NR_UE/nr_nas_msg.h
index c52096107d..2e687baf8f 100644
--- a/openair3/NAS/NR_UE/nr_nas_msg_sim.h
+++ b/openair3/NAS/NR_UE/nr_nas_msg.h
@@ -19,7 +19,7 @@
* contact@openairinterface.org
*/
-/*! \file nr_nas_msg_sim.h
+/*! \file nr_nas_msg.h
* \brief simulator for nr nas message
* \author Yoshio INOUE, Masayuki HARADA
* \email yoshio.inoue@fujitsu.com,masayuki.harada@fujitsu.com
@@ -27,8 +27,6 @@
* \version 0.1
*/
-
-
#ifndef __NR_NAS_MSG_SIM_H__
#define __NR_NAS_MSG_SIM_H__
@@ -43,37 +41,12 @@
#include
#include "secu_defs.h"
-#define PLAIN_5GS_MSG 0b0000
-#define INTEGRITY_PROTECTED 0b0001
-#define INTEGRITY_PROTECTED_AND_CIPHERED 0b0010
-#define INTEGRITY_PROTECTED_WITH_NEW_SECU_CTX 0b0011 // only for SECURITY MODE COMMAND
-#define INTEGRITY_PROTECTED_AND_CIPHERED_WITH_NEW_SECU_CTX 0b0100 // only for SECURITY MODE COMPLETE
+#define INITIAL_REGISTRATION 0b001
-#define REGISTRATION_REQUEST 0b01000001 /* 65 = 0x41 */
-#define REGISTRATION_ACCEPT 0b01000010 /* 66 = 0x42 */
-#define REGISTRATION_COMPLETE 0b01000011 /* 67 = 0x43 */
-#define FGS_DEREGISTRATION_REQUEST_UE_ORIGINATING 0b01000101
-#define FGS_DEREGISTRATION_ACCEPT 0b01000110
-#define FGS_AUTHENTICATION_REQUEST 0b01010110 /* 86 = 0x56 */
-#define FGS_AUTHENTICATION_RESPONSE 0b01010111 /* 87 = 0x57 */
-#define FGS_IDENTITY_REQUEST 0b01011011 /* 91 = 0x5b */
-#define FGS_IDENTITY_RESPONSE 0b01011100 /* 92 = 0x5c */
-#define FGS_SECURITY_MODE_COMMAND 0b01011101 /* 93 = 0x5d */
-#define FGS_SECURITY_MODE_COMPLETE 0b01011110 /* 94 = 0x5e */
-#define FGS_UPLINK_NAS_TRANSPORT 0b01100111 /* 103= 0x67 */
-#define FGS_DOWNLINK_NAS_TRANSPORT 0b01101000 /* 104= 0x68 */
-
-// message type for 5GS session management
-#define FGS_PDU_SESSION_ESTABLISHMENT_REQ 0b11000001 /* 193= 0xc1 */
-#define FGS_PDU_SESSION_ESTABLISHMENT_ACC 0b11000010 /* 194= 0xc2 */
-#define FGS_PDU_SESSION_ESTABLISHMENT_REJ 0b11000011 /* 195= 0xc3 */
-
-#define INITIAL_REGISTRATION 0b001
-
-#define PLAIN_5GS_NAS_MESSAGE_HEADER_LENGTH 3
-#define SECURITY_PROTECTED_5GS_NAS_MESSAGE_HEADER_LENGTH 7
-#define PAYLOAD_CONTAINER_LENGTH_MIN 3
-#define PAYLOAD_CONTAINER_LENGTH_MAX 65537
+#define PLAIN_5GS_NAS_MESSAGE_HEADER_LENGTH 3
+#define SECURITY_PROTECTED_5GS_NAS_MESSAGE_HEADER_LENGTH 7
+#define PAYLOAD_CONTAINER_LENGTH_MIN 3
+#define PAYLOAD_CONTAINER_LENGTH_MAX 65537
/* List of allowed NSSAI from NAS messaging. */
typedef struct {
@@ -103,20 +76,19 @@ typedef struct {
stream_security_container_t *security_container;
Guti5GSMobileIdentity_t *guti;
bool termination_procedure;
- uint8_t *registration_request_buf;
- uint32_t registration_request_len;
+ uint8_t *registration_request_buf;
+ uint32_t registration_request_len;
instance_t UE_id;
} nr_ue_nas_t;
typedef enum fgs_protocol_discriminator_e {
/* Protocol discriminator identifier for 5GS Mobility Management */
- FGS_MOBILITY_MANAGEMENT_MESSAGE = 0x7E,
+ FGS_MOBILITY_MANAGEMENT_MESSAGE = 0x7E,
/* Protocol discriminator identifier for 5GS Session Management */
- FGS_SESSION_MANAGEMENT_MESSAGE = 0x2E,
+ FGS_SESSION_MANAGEMENT_MESSAGE = 0x2E,
} fgs_protocol_discriminator_t;
-
typedef struct {
uint8_t ex_protocol_discriminator;
uint8_t security_header_type;
@@ -125,27 +97,25 @@ typedef struct {
/* Structure of security protected header */
typedef struct {
- fgs_protocol_discriminator_t protocol_discriminator;
- uint8_t security_header_type;
- uint32_t message_authentication_code;
- uint8_t sequence_number;
+ fgs_protocol_discriminator_t protocol_discriminator;
+ uint8_t security_header_type;
+ uint32_t message_authentication_code;
+ uint8_t sequence_number;
} fgs_nas_message_security_header_t;
typedef union {
- mm_msg_header_t header;
- registration_request_msg registration_request;
- fgs_identiy_response_msg fgs_identity_response;
- fgs_authentication_response_msg fgs_auth_response;
+ mm_msg_header_t header;
+ registration_request_msg registration_request;
+ fgs_identiy_response_msg fgs_identity_response;
+ fgs_authentication_response_msg fgs_auth_response;
fgs_deregistration_request_ue_originating_msg fgs_deregistration_request_ue_originating;
- fgs_security_mode_complete_msg fgs_security_mode_complete;
- registration_complete_msg registration_complete;
- fgs_uplink_nas_transport_msg uplink_nas_transport;
+ fgs_security_mode_complete_msg fgs_security_mode_complete;
+ registration_complete_msg registration_complete;
+ fgs_uplink_nas_transport_msg uplink_nas_transport;
} MM_msg;
-
-
typedef struct {
- MM_msg mm_msg; /* 5GS Mobility Management messages */
+ MM_msg mm_msg; /* 5GS Mobility Management messages */
} fgs_nas_message_plain_t;
typedef struct {
@@ -153,7 +123,6 @@ typedef struct {
fgs_nas_message_plain_t plain;
} fgs_nas_message_security_protected_t;
-
typedef union {
fgs_nas_message_security_header_t header;
fgs_nas_message_security_protected_t security_protected;
@@ -164,11 +133,11 @@ typedef struct {
union {
mm_msg_header_t plain_nas_msg_header;
struct security_protected_nas_msg_header_s {
- uint8_t ex_protocol_discriminator;
- uint8_t security_header_type;
+ uint8_t ex_protocol_discriminator;
+ uint8_t security_header_type;
uint16_t message_authentication_code1;
uint16_t message_authentication_code2;
- uint8_t sequence_number;
+ uint8_t sequence_number;
} security_protected_nas_msg_header_t;
} choice;
} nas_msg_header_t;
@@ -181,10 +150,10 @@ typedef struct {
} fgs_sm_nas_msg_header_t;
typedef struct {
- mm_msg_header_t plain_nas_msg_header;
- uint8_t payload_container_type;
- uint16_t payload_container_length;
- fgs_sm_nas_msg_header_t sm_nas_msg_header;
+ mm_msg_header_t plain_nas_msg_header;
+ uint8_t payload_container_type;
+ uint16_t payload_container_length;
+ fgs_sm_nas_msg_header_t sm_nas_msg_header;
} dl_nas_transport_t;
nr_ue_nas_t *get_ue_nas_info(module_id_t module_id);
@@ -193,6 +162,3 @@ void *nas_nrue_task(void *args_p);
void *nas_nrue(void *args_p);
#endif /* __NR_NAS_MSG_SIM_H__*/
-
-
-
diff --git a/openair3/NAS/NR_UE/ue_process_nas.c b/openair3/NAS/NR_UE/ue_process_nas.c
deleted file mode 100644
index a89a07218e..0000000000
--- a/openair3/NAS/NR_UE/ue_process_nas.c
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * 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
- *
- * Author and copyright: Laurent Thomas, open-cells.com
- *
- * 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
-#include
-
-
-void SGSabortNet(void *msg, nr_user_nas_t *UE) {
-}
-
-void ue_nas_schedule(void) {
-}
-
-/*
- *Message reception
- */
-
-void SGSauthenticationReq(void *msg, nr_user_nas_t *UE) {
- authenticationrequestHeader_t *amsg=(authenticationrequestHeader_t *) msg;
- arrayCpy(UE->uicc->rand,amsg->RAND);
- arrayCpy(UE->uicc->autn,amsg->AUTN);
- // AUTHENTICATION REQUEST message that contains a valid ngKSI, SQN and MAC is received
- // TBD verify ngKSI (we set it as '2', see gNB code)
- // SQN and MAC are tested in auth resp processing
- ue_nas_schedule();
-}
-
-void SGSidentityReq(void *msg, nr_user_nas_t *UE) {
- Identityrequest_t *idmsg=(Identityrequest_t *) msg;
-
- if (idmsg->it == SUCI ) {
- LOG_I(NAS,"Received Identity request, scheduling answer\n");
- ue_nas_schedule();
- } else
- LOG_E(NAS,"Not developped: identity request for %d\n", idmsg->it);
-}
-
-void SGSsecurityModeCommand(void *msg, nr_user_nas_t *UE) {
-}
-
-
-void UEprocessNAS(void *msg,nr_user_nas_t *UE) {
- SGScommonHeader_t *header=(SGScommonHeader_t *) msg;
-
- if ( header->sh > 4 )
- SGSabortNet(msg, UE);
- else {
- switch (header->epd) {
- case SGSmobilitymanagementmessages:
- LOG_I(NAS,"Received message: %s\n", idStr(message_text_info, header->mt));
-
- switch (header->mt) {
- case Authenticationrequest:
- SGSauthenticationReq(msg, UE);
- break;
-
- case Identityrequest:
- SGSidentityReq(msg, UE);
- break;
-
- case Securitymodecommand:
- SGSsecurityModeCommand(msg, UE);
- break;
-
- default:
- SGSabortNet(msg, UE);
- }
-
- break;
-
- case SGSsessionmanagementmessages:
- SGSabortNet(msg, UE);
- break;
-
- default:
- SGSabortNet(msg, UE);
- }
- }
-}
-
-/*
- * Messages emission
- */
-
-int identityResponse(void **msg, nr_user_nas_t *UE) {
- if (UE->uicc == NULL)
- // config file section hardcoded as "uicc", nevertheless it opens to manage several UEs or a multi SIM UE
- UE->uicc=init_uicc("uicc");
-
- // TS 24.501 9.11.3.4
- int imsiL=strlen(UE->uicc->imsiStr);
- int msinL=imsiL-3-UE->uicc->nmc_size;
- int respSize=sizeof(IdentityresponseIMSI_t) + (msinL+1)/2;
- IdentityresponseIMSI_t *resp=(IdentityresponseIMSI_t *) calloc(respSize,1);
- resp->common.epd=SGSmobilitymanagementmessages;
- resp->common.sh=0;
- resp->common.mt=Identityresponse;
- resp->common.len=htons(respSize-sizeof(Identityresponse_t));
- resp->mi=SUCI;
- resp->mcc1=UE->uicc->imsiStr[0]-'0';
- resp->mcc2=UE->uicc->imsiStr[1]-'0';
- resp->mcc3=UE->uicc->imsiStr[2]-'0';
- resp->mnc1=UE->uicc->imsiStr[3]-'0';
- resp->mnc2=UE->uicc->imsiStr[4]-'0';
- resp->mnc3=UE->uicc->nmc_size==2? 0xF : UE->uicc->imsiStr[3]-'0';
- // TBD: routing to fill (FF ?)
- char *out=(char *)(resp+1);
- char *ptr=UE->uicc->imsiStr + 3 + UE->uicc->nmc_size;
-
- while ( ptr < UE->uicc->imsiStr+strlen(UE->uicc->imsiStr) ) {
- *out=((*(ptr+1)-'0')<<4) | (*(ptr) -'0');
- out++;
- ptr+=2;
- }
-
- if (msinL%2 == 1)
- *out=((*(ptr-1)-'0')) | 0xF0;
-
- *msg=resp;
- return respSize;
-}
-
-int authenticationResponse(void **msg,nr_user_nas_t *UE) {
- if (UE->uicc == NULL)
- // config file section hardcoded as "uicc", nevertheless it opens to manage several UEs or a multi SIM UE
- UE->uicc=init_uicc("uicc");
-
- myCalloc(resp, authenticationresponse_t);
- resp->epd=SGSmobilitymanagementmessages;
- resp->sh=0;
- resp->mt=Authenticationresponse;
- resp->iei=IEI_AuthenticationResponse;
- resp->RESlen=sizeof(resp->RES); // always 16 see TS 24.501 TableĀ 8.2.2.1.1
- // Verify the AUTN
- // a full implementation need to test several SQN
- // as the last 5bits can be any value
- // and the value can also be greater and accepted (if it is in the accepted window)
- uint8_t AUTN[16];
- uicc_milenage_generate(AUTN, UE->uicc);
-
- if ( memcmp(UE->uicc->autn, AUTN, sizeof(AUTN)) == 0 ) {
- // prepare and send good answer
- resToresStar(resp->RES,UE->uicc);
- } else {
- // prepare and send autn is not compatible with our data
- abort();
- }
-
- *msg=resp;
- return sizeof(authenticationresponse_t);
-}
-
-int securityModeComplete(void **msg, nr_user_nas_t *UE) {
- return -1;
-}
-
-int registrationComplete(void **msg, nr_user_nas_t *UE) {
- return -1;
-}
diff --git a/openair3/NAS/gNB/network_process_nas.c b/openair3/NAS/gNB/network_process_nas.c
deleted file mode 100644
index ce8897730b..0000000000
--- a/openair3/NAS/gNB/network_process_nas.c
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
- * 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
- *
- * Author and copyright: Laurent Thomas, open-cells.com
- *
- * 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
-
-void SGSabortUE(void *msg, NRUEcontext_t *UE) {
-}
-void nas_schedule(void) {
-}
-/*
- *Message reception
- */
-
-void SGSregistrationReq(void *msg, NRUEcontext_t *UE) {
-}
-
-void SGSderegistrationUEReq(void *msg, NRUEcontext_t *UE) {
-}
-
-void SGSidentityResp(void *msg, NRUEcontext_t *UE) {
- // use the UE SUPI to load the USIM
- IdentityresponseIMSI_t *resp=(IdentityresponseIMSI_t *)msg;
- char configName[100]={0};
- char * ptr= stpcpy(configName,"uicc");
- *ptr++=resp->mcc1+'0';
- *ptr++=resp->mcc2+'0';
- *ptr++=resp->mcc3+'0';
- *ptr++=resp->mnc1+'0';
- *ptr++=resp->mnc2+'0';
- if ( resp->mnc3 != 0xF )
- *ptr++=resp->mnc3 +'0';
- int respSize=ntohs(resp->common.len);
- int msinByteSize=respSize-sizeof(IdentityresponseIMSI_t);
- uint8_t * msin=(uint8_t *) (resp+1);
- for(int i=3 ; i> 4) ;
- *ptr++=(msin[i] & 0xf) +'0';
- if (digit1 != 0xf)
- *ptr++=digit1 +'0';
- }
- *ptr=0;
- if (UE->uicc == NULL)
- // config file section hardcoded as "uicc", nevertheless it opens to manage several UEs or a multi SIM UE
- UE->uicc=init_uicc(configName);
- //We schedule Authentication request
- nas_schedule();
-}
-
-void SGSauthenticationResp(void *msg, NRUEcontext_t *UE) {
- // Let's check the answer
- authenticationresponse_t* resp=(authenticationresponse_t*) msg;
- uint8_t resStarLocal[16];
- resToresStar(resStarLocal,UE->uicc);
- if ( memcmp(resStarLocal, resp->RES, sizeof(resStarLocal)) == 0 ) {
- LOG_I(NAS,"UE answer to authentication resquest is correct\n");
- // we will send securityModeCommand
- nas_schedule();
- } else {
- LOG_E(NAS,"UE authentication response not good\n");
- // To be implemented ?
- }
-}
-
-void SGSsecurityModeComplete(void *msg, NRUEcontext_t *UE) {
-
-}
-
-void SGSregistrationComplete(void *msg, NRUEcontext_t *UE) {
-}
-
-void processNAS(void *msg, NRUEcontext_t *UE) {
- SGScommonHeader_t *header=(SGScommonHeader_t *) msg;
-
- if ( header->sh > 4 )
- SGSabortUE(msg, UE);
- else {
- switch (header->epd) {
- case SGSmobilitymanagementmessages:
- LOG_I(NAS,"Received message: %s\n", idStr(message_text_info, header->mt));
-
- switch (header->mt) {
- case Registrationrequest:
- SGSregistrationReq(msg, UE);
- break;
-
- case DeregistrationrequestUEoriginating:
- SGSderegistrationUEReq(msg, UE);
- break;
-
- case Authenticationresponse:
- SGSauthenticationResp(msg, UE);
- break;
-
- case Identityresponse:
- SGSidentityResp(msg, UE);
- break;
-
- case Securitymodecomplete:
- SGSsecurityModeComplete(msg, UE);
- break;
-
- case Registrationcomplete:
- SGSregistrationComplete(msg, UE);
- break;
-
- default:
- SGSabortUE(msg, UE);
- }
-
- break;
-
- case SGSsessionmanagementmessages:
- SGSabortUE(msg, UE);
- break;
-
- default:
- SGSabortUE(msg, UE);
- }
- }
-}
-
-/*
- * Messages emission
- */
-
-int identityRequest(void **msg, NRUEcontext_t *UE) {
- myCalloc(req, Identityrequest_t);
- req->epd=SGSmobilitymanagementmessages;
- req->sh=0;
- req->mt=Identityrequest;
- req->it=SUCI;
- *msg=req;
- return sizeof(Identityrequest_t);
-}
-
-int authenticationRequest(void **msg, NRUEcontext_t *UE) {
-
- myCalloc(req, authenticationrequestHeader_t);
- req->epd=SGSmobilitymanagementmessages;
- req->sh=0;
- req->mt=Authenticationrequest;
- // native security context => bit 4 to 0
- // probably from TS 33.501, table A-8.1
- // N-NAS-int-alg (Native NAS integrity)
- /*
- N-NAS-enc-alg 0x01
- N-NAS-int-alg 0x02
- N-RRC-enc-alg 0x03
- N-RRC-int-alg 0x04
- N-UP-enc-alg 0x05
- N-UP-int-alg 0x06
- */
- req->ngKSI=2;
- // TS 33.501, Annex A.7.1: Initial set of security features defined for 5GS.
- req->ABBALen=2;
- req->ABBA=0;
- //rand (TV)
- req->ieiRAND=IEI_RAND;
- FILE *h=fopen("/dev/random","r");
-
- if ( sizeof(req->RAND) != fread(req->RAND,1,sizeof(req->RAND),h) )
- LOG_E(NAS, "can't read /dev/random\n");
-
- fclose(h);
- memcpy(UE->uicc->rand, req->RAND, sizeof(UE->uicc->rand));
- // challenge/AUTN (TLV)
- req->ieiAUTN=IEI_AUTN;
- req->AUTNlen=sizeof(req->AUTN);
- uicc_milenage_generate( req->AUTN, UE->uicc);
- // EAP message (TLV-E)
- // not developped
- *msg=req;
- return sizeof(authenticationrequestHeader_t);
-}
-
-int securityModeCommand(void **msg, NRUEcontext_t *UE) {
- *msg=NULL;
- myCalloc(req, securityModeCommand_t);
- req->epd=SGSmobilitymanagementmessages;
- req->sh=0;
- req->mt=Securitymodecommand;
-
- // integrity algo from 5G-IA0 (null algo) to 5G-IA7 in first 4 bits
- unsigned int ia=0;
- // cyphering algo from 5G-EA0 (null algo) to 5G-IA7 in MSB 4 bits
- unsigned int ea=0;
- // trace from commercial: 5G-IA0 (0) 128-5G-IA2 (2)
- req->selectedNASsecurityalgorithms= ea<<4 | ia;
-
- // KSI: N-NAS-int-alg 0x02
- req->ngKSI=2;
-
- *msg=req;
- return 0;
-}
diff --git a/radio/rfsimulator/CMakeLists.txt b/radio/rfsimulator/CMakeLists.txt
index efba710bad..61b053a1ce 100644
--- a/radio/rfsimulator/CMakeLists.txt
+++ b/radio/rfsimulator/CMakeLists.txt
@@ -3,7 +3,7 @@ add_library(rfsimulator MODULE
apply_channelmod.c
../../openair1/PHY/TOOLS/signal_energy.c
)
-target_link_libraries(rfsimulator PRIVATE SIMU)
+target_link_libraries(rfsimulator PRIVATE SIMU HASHTABLE)
set_target_properties(rfsimulator PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
add_executable(replay_node stored_node.c)
diff --git a/radio/rfsimulator/simulator.c b/radio/rfsimulator/simulator.c
index 3c19b492c8..d362d5ef7b 100644
--- a/radio/rfsimulator/simulator.c
+++ b/radio/rfsimulator/simulator.c
@@ -50,6 +50,7 @@
#define CHANNELMOD_DYNAMICLOAD
#include
#include "rfsimulator.h"
+#include "hashtable.h"
#define PORT 4043 //default TCP port for this simulator
//
@@ -82,6 +83,8 @@
// Until a convenient management is implemented, the MAX_FD_RFSIMU is used everywhere (instead of
// FD_SETSIE) and reduced to 125. This should allow for around 20 simultaeous UEs.
//
+// An indirection level via hashtable was added to allow the software to use FDs above MAX_FD_RFSIMU.
+//
// #define MAX_FD_RFSIMU FD_SETSIZE
#define MAX_FD_RFSIMU 250
#define SEND_BUFF_SIZE 100000000 // Socket buffer size
@@ -162,6 +165,9 @@ typedef struct {
uint16_t port;
int saveIQfile;
buffer_t buf[MAX_FD_RFSIMU];
+ int next_buf;
+ // Hashtable used as an indirection level between file descriptor and the buf array
+ hash_table_t *fd_to_buf_map;
int rx_num_channels;
int tx_num_channels;
double sample_rate;
@@ -178,11 +184,38 @@ typedef struct {
double prop_delay_ms;
} rfsimulator_state_t;
+
+static buffer_t *get_buff_from_socket(rfsimulator_state_t *simulator_state, int socket)
+{
+ uint64_t buffer_index;
+ if (hashtable_get(simulator_state->fd_to_buf_map, socket, (void **)&buffer_index) == HASH_TABLE_OK) {
+ return &simulator_state->buf[buffer_index];
+ } else {
+ return NULL;
+ }
+}
+
+static void add_buff_to_socket_mapping(rfsimulator_state_t *simulator_state, int socket, uint64_t buff_index)
+{
+ hashtable_rc_t rc = hashtable_insert(simulator_state->fd_to_buf_map, socket, (void *)buff_index);
+ AssertFatal(rc == HASH_TABLE_OK,
+ "%s sock = %d\n",
+ rc == HASH_TABLE_INSERT_OVERWRITTEN_DATA ? "Duplicate entry in hashtable" : "Hashtable is not allocated",
+ socket);
+}
+
+static void remove_buff_to_socket_mapping(rfsimulator_state_t *simulator_state, int socket)
+{
+ // Failure is fine here
+ hashtable_remove(simulator_state->fd_to_buf_map, socket);
+}
+
static int allocCirBuf(rfsimulator_state_t *bridge, int sock)
{
/* TODO: cleanup code so that this AssertFatal becomes useless */
AssertFatal(sock >= 0 && sock < sizeofArray(bridge->buf), "socket %d is not in range\n", sock);
- buffer_t *ptr=&bridge->buf[sock];
+ uint64_t buff_index = bridge->next_buf++ % MAX_FD_RFSIMU;
+ buffer_t *ptr=&bridge->buf[buff_index];
ptr->circularBuf = calloc(1, sampleToByte(CirSize, 1));
if (ptr->circularBuf == NULL) {
LOG_E(HW, "malloc(%lu) failed\n", sampleToByte(CirSize, 1));
@@ -235,6 +268,7 @@ static int allocCirBuf(rfsimulator_state_t *bridge, int sock)
random_channel(ptr->channel_model,false);
LOG_I(HW, "Random channel %s in rfsimulator activated\n", modelname);
}
+ add_buff_to_socket_mapping(bridge, sock, buff_index);
return 0;
}
@@ -243,17 +277,23 @@ static void removeCirBuf(rfsimulator_state_t *bridge, int sock) {
LOG_E(HW, "epoll_ctl(EPOLL_CTL_DEL) failed\n");
}
close(sock);
- free(bridge->buf[sock].circularBuf);
- // Fixme: no free_channel_desc_scm(bridge->buf[sock].channel_model) implemented
- // a lot of mem leaks
- //free(bridge->buf[sock].channel_model);
- memset(&bridge->buf[sock], 0, sizeof(buffer_t));
- bridge->buf[sock].conn_sock=-1;
- nb_ue--;
+ buffer_t* buf = get_buff_from_socket(bridge, sock);
+ if (buf) {
+ free(buf->circularBuf);
+ // Fixme: no free_channel_desc_scm(bridge->buf[sock].channel_model) implemented
+ // a lot of mem leaks
+ //free(bridge->buf[sock].channel_model);
+ memset(buf, 0, sizeof(buffer_t));
+ buf->conn_sock=-1;
+ remove_buff_to_socket_mapping(bridge, sock);
+ nb_ue--;
+ }
}
static void socketError(rfsimulator_state_t *bridge, int sock) {
- if (bridge->buf[sock].conn_sock!=-1) {
+ buffer_t* buf = get_buff_from_socket(bridge, sock);
+ if (!buf) return;
+ if (buf->conn_sock != -1) {
LOG_W(HW, "Lost socket\n");
removeCirBuf(bridge, sock);
@@ -789,7 +829,7 @@ static bool flushInput(rfsimulator_state_t *t, int timeout, int nsamps_for_initi
rfsimulator_write_internal(t, t->lastWroteTS > 1 ? t->lastWroteTS - 1 : 0, samplesVoid, 1, t->tx_num_channels, 1, false);
- buffer_t *b = &t->buf[conn_sock];
+ buffer_t *b = get_buff_from_socket(t, conn_sock);
if (b->channel_model)
b->channel_model->start_TS = t->lastWroteTS;
} else {
@@ -798,8 +838,8 @@ static bool flushInput(rfsimulator_state_t *t, int timeout, int nsamps_for_initi
continue;
}
- buffer_t *b=&t->buf[fd];
-
+ buffer_t *b = get_buff_from_socket(t, fd);
+ if (!b) continue;
if ( b->circularBuf == NULL ) {
LOG_E(HW, "Received data on not connected socket %d\n", events[nbEv].data.fd);
continue;
@@ -1044,6 +1084,7 @@ static void rfsimulator_end(openair0_device *device) {
removeCirBuf(s, b->conn_sock);
}
close(s->epollfd);
+ hashtable_destroy(&s->fd_to_buf_map);
}
static int rfsimulator_stop(openair0_device *device) {
return 0;
@@ -1059,6 +1100,12 @@ static int rfsimulator_set_gains(openair0_device *device, openair0_config_t *ope
static int rfsimulator_write_init(openair0_device *device) {
return 0;
}
+
+void do_not_free_integer(void *integer)
+{
+ (void)integer;
+}
+
__attribute__((__visibility__("default")))
int device_init(openair0_device *device, openair0_config_t *openair0_cfg) {
// to change the log level, use this on command line
@@ -1104,6 +1151,8 @@ int device_init(openair0_device *device, openair0_config_t *openair0_cfg) {
for (int i = 0; i < MAX_FD_RFSIMU; i++)
rfsimulator->buf[i].conn_sock=-1;
+ rfsimulator->next_buf = 0;
+ rfsimulator->fd_to_buf_map = hashtable_create(MAX_FD_RFSIMU, NULL, do_not_free_integer);
AssertFatal((rfsimulator->epollfd = epoll_create1(0)) != -1, "epoll_create1() failed, errno(%d)", errno);
// we need to call randominit() for telnet server (use gaussdouble=>uniformrand)
diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-cu.sa.f1.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-cu.sa.f1.conf
new file mode 100644
index 0000000000..08b72bf6c6
--- /dev/null
+++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-cu.sa.f1.conf
@@ -0,0 +1,74 @@
+Active_gNBs = ( "cu-rfsim");
+# Asn1_verbosity, choice in: none, info, annoying
+Asn1_verbosity = "none";
+Num_Threads_PUSCH = 8;
+
+gNBs =
+(
+ {
+ ////////// Identification parameters:
+ gNB_ID = 0xe00;
+ gNB_name = "cu-rfsim";
+
+ // Tracking area code, 0x0000 and 0xfffe are reserved values
+ tracking_area_code = 1;
+ plmn_list = ({ mcc = 001; mnc = 01; mnc_length = 2; snssaiList = ({ sst = 1; }) });
+
+ nr_cellid = 12345678L;
+
+ tr_s_preference = "f1";
+
+ local_s_if_name = "lo";
+ local_s_address = "127.0.0.3";
+ remote_s_address = "0.0.0.0"; # multiple DUs
+ local_s_portc = 501;
+ local_s_portd = 2152;
+ remote_s_portc = 500;
+ remote_s_portd = 2152;
+
+
+ # ------- SCTP definitions
+ SCTP :
+ {
+ # Number of streams to use in input/output
+ SCTP_INSTREAMS = 2;
+ SCTP_OUTSTREAMS = 2;
+ };
+
+
+ ////////// AMF parameters:
+ amf_ip_address = ({ ipv4 = "192.168.70.132"; });
+
+ NETWORK_INTERFACES :
+ {
+ GNB_IPV4_ADDRESS_FOR_NG_AMF = "192.168.70.129/24";
+ GNB_IPV4_ADDRESS_FOR_NGU = "192.168.70.129/24";
+ GNB_PORT_FOR_S1U = 2152; # Spec 2152
+ };
+ }
+);
+
+security = {
+ # preferred ciphering algorithms
+ # the first one of the list that an UE supports in chosen
+ # valid values: nea0, nea1, nea2, nea3
+ ciphering_algorithms = ( "nea0" );
+
+ # preferred integrity algorithms
+ # the first one of the list that an UE supports in chosen
+ # valid values: nia0, nia1, nia2, nia3
+ integrity_algorithms = ( "nia2", "nia0" );
+
+ # setting 'drb_ciphering' to "no" disables ciphering for DRBs, no matter
+ # what 'ciphering_algorithms' configures; same thing for 'drb_integrity'
+ drb_ciphering = "yes";
+ drb_integrity = "no";
+};
+
+log_config : {
+ global_log_level = "info";
+ pdcp_log_level = "info";
+ rrc_log_level = "info";
+ f1ap_log_level = "info";
+ ngap_log_level = "info";
+};
diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band66.25prb.rfsim.pci0.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band66.25prb.rfsim.pci0.conf
new file mode 100644
index 0000000000..abd624747d
--- /dev/null
+++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band66.25prb.rfsim.pci0.conf
@@ -0,0 +1,215 @@
+Active_gNBs = ( "du-rfsim");
+# Asn1_verbosity, choice in: none, info, annoying
+Asn1_verbosity = "none";
+
+gNBs =
+(
+ {
+ ////////// Identification parameters:
+ gNB_ID = 0xe00;
+ gNB_DU_ID = 0xe00;
+ gNB_name = "du-rfsim";
+
+ // Tracking area code, 0x0000 and 0xfffe are reserved values
+ tracking_area_code = 1;
+ plmn_list = ({ mcc = 208; mnc = 95; mnc_length = 2; snssaiList = ({ sst = 1; }) });
+
+ nr_cellid = 12345678L;
+
+ ////////// Physical parameters:
+
+ min_rxtxtime = 4;
+ sib1_tda = 15;
+
+ servingCellConfigCommon = (
+ {
+ #spCellConfigCommon
+
+ physCellId = 0;
+
+# downlinkConfigCommon
+ #frequencyInfoDL
+ # this is 3600 MHz + 43 PRBs@30kHz SCS (same as initial BWP)
+ absoluteFrequencySSB = 430610;
+ dl_frequencyBand = 66;
+ # this is 3600 MHz
+ dl_absoluteFrequencyPointA = 430000;
+ #scs-SpecificCarrierList
+ dl_offstToCarrier = 0;
+# subcarrierSpacing
+# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
+ dl_subcarrierSpacing = 0;
+ dl_carrierBandwidth = 25;
+ #initialDownlinkBWP
+ #genericParameters
+ # this is RBstart=27,L=48 (275*(L-1))+RBstart
+ initialDLBWPlocationAndBandwidth = 6600; # 6366 12925 12956 28875 12952
+# subcarrierSpacing
+# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
+ initialDLBWPsubcarrierSpacing = 0;
+ #pdcch-ConfigCommon
+ initialDLBWPcontrolResourceSetZero = 0;
+ initialDLBWPsearchSpaceZero = 0;
+
+ #uplinkConfigCommon
+ #frequencyInfoUL
+ ul_frequencyBand = 66;
+ # this is 1750 MHz
+ ul_absoluteFrequencyPointA = 350000;
+ #scs-SpecificCarrierList
+ ul_offstToCarrier = 0;
+# subcarrierSpacing
+# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
+ ul_subcarrierSpacing = 0;
+ ul_carrierBandwidth = 25;
+ pMax = 20;
+ #initialUplinkBWP
+ #genericParameters
+ initialULBWPlocationAndBandwidth = 6600;
+# subcarrierSpacing
+# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
+ initialULBWPsubcarrierSpacing = 0;
+ #rach-ConfigCommon
+ #rach-ConfigGeneric
+ prach_ConfigurationIndex = 98;
+#prach_msg1_FDM
+#0 = one, 1=two, 2=four, 3=eight
+ prach_msg1_FDM = 0;
+ prach_msg1_FrequencyStart = 0;
+ zeroCorrelationZoneConfig = 13;
+ preambleReceivedTargetPower = -96;
+#preamblTransMax (0...10) = (3,4,5,6,7,8,10,20,50,100,200)
+ preambleTransMax = 6;
+#powerRampingStep
+# 0=dB0,1=dB2,2=dB4,3=dB6
+ powerRampingStep = 1;
+#ra_ReponseWindow
+#1,2,4,8,10,20,40,80
+ ra_ResponseWindow = 4;
+#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
+#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
+ ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;
+#oneHalf (0..15) 4,8,12,16,...60,64
+ ssb_perRACH_OccasionAndCB_PreamblesPerSSB = 15;
+#ra_ContentionResolutionTimer
+#(0..7) 8,16,24,32,40,48,56,64
+ ra_ContentionResolutionTimer = 7;
+ rsrp_ThresholdSSB = 19;
+#prach-RootSequenceIndex_PR
+#1 = 839, 2 = 139
+ prach_RootSequenceIndex_PR = 2;
+ prach_RootSequenceIndex = 1;
+ # SCS for msg1, can only be 15 for 30 kHz < 6 GHz, takes precendence over the one derived from prach-ConfigIndex
+ #
+ msg1_SubcarrierSpacing = 0,
+# restrictedSetConfig
+# 0=unrestricted, 1=restricted type A, 2=restricted type B
+ restrictedSetConfig = 0,
+
+ msg3_DeltaPreamble = 1;
+ p0_NominalWithGrant =-90;
+
+# pucch-ConfigCommon setup :
+# pucchGroupHopping
+# 0 = neither, 1= group hopping, 2=sequence hopping
+ pucchGroupHopping = 0;
+ hoppingId = 40;
+ p0_nominal = -90;
+
+ ssb_PositionsInBurst_Bitmap = 1;
+
+# ssb_periodicityServingCell
+# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1
+ ssb_periodicityServingCell = 2;
+
+# dmrs_TypeA_position
+# 0 = pos2, 1 = pos3
+ dmrs_TypeA_Position = 0;
+
+# subcarrierSpacing
+# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
+ subcarrierSpacing = 0;
+
+
+ #tdd-UL-DL-ConfigurationCommon
+# subcarrierSpacing
+# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
+ referenceSubcarrierSpacing = 0;
+ # pattern1
+ # dl_UL_TransmissionPeriodicity
+ # 0=ms0p5, 1=ms0p625, 2=ms1, 3=ms1p25, 4=ms2, 5=ms2p5, 6=ms5, 7=ms10
+ dl_UL_TransmissionPeriodicity = 6;
+ nrofDownlinkSlots = 7;
+ nrofDownlinkSymbols = 6;
+ nrofUplinkSlots = 2;
+ nrofUplinkSymbols = 4;
+
+ ssPBCH_BlockPower = -25;
+ }
+
+ );
+
+ # ------- SCTP definitions
+ SCTP :
+ {
+ # Number of streams to use in input/output
+ SCTP_INSTREAMS = 2;
+ SCTP_OUTSTREAMS = 2;
+ };
+ }
+);
+
+MACRLCs = ({
+ num_cc = 1;
+ tr_s_preference = "local_L1";
+ tr_n_preference = "f1";
+ local_n_if_name = "lo";
+ local_n_address = "127.0.0.4";
+ remote_n_address = "127.0.0.3";
+ local_n_portc = 500;
+ local_n_portd = 2152;
+ remote_n_portc = 501;
+ remote_n_portd = 2152;
+ pusch_FailureThres = 1000;
+});
+
+L1s = ({
+ num_cc = 1;
+ tr_n_preference = "local_mac";
+ prach_dtx_threshold = 200;
+ pucch0_dtx_threshold = 150;
+ ofdm_offset_divisor = 8; #set this to UINT_MAX for offset 0
+});
+
+RUs = ({
+ local_rf = "yes"
+ nb_tx = 1
+ nb_rx = 1
+ att_tx = 0
+ att_rx = 0;
+ bands = [66];
+ max_pdschReferenceSignalPower = -27;
+ max_rxgain = 114;
+ eNB_instances = [0];
+ clock_src = "internal";
+});
+
+rfsimulator: {
+ serveraddr = "server";
+ serverport = 4043;
+ options = (); #("saviq"); or/and "chanmod"
+ modelname = "AWGN";
+ IQfile = "/tmp/rfsimulator.iqs"
+}
+
+log_config: {
+ global_log_level = "info";
+ hw_log_level = "info";
+ nr_phy_log_level = "info";
+ nr_mac_log_level = "info";
+ rlc_log_level = "info";
+ pdcp_log_level = "info";
+ rrc_log_level = "info";
+ f1ap_log_level = "info";
+ ngap_log_level = "debug";
+};
diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band66.25prb.rfsim.pci1.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band66.25prb.rfsim.pci1.conf
new file mode 100644
index 0000000000..8d2e46b44e
--- /dev/null
+++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band66.25prb.rfsim.pci1.conf
@@ -0,0 +1,215 @@
+Active_gNBs = ( "du-rfsim");
+# Asn1_verbosity, choice in: none, info, annoying
+Asn1_verbosity = "none";
+
+gNBs =
+(
+ {
+ ////////// Identification parameters:
+ gNB_ID = 0xe00;
+ gNB_DU_ID = 0xe01;
+ gNB_name = "du-rfsim";
+
+ // Tracking area code, 0x0000 and 0xfffe are reserved values
+ tracking_area_code = 1;
+ plmn_list = ({ mcc = 208; mnc = 95; mnc_length = 2; snssaiList = ({ sst = 1; }) });
+
+ nr_cellid = 11111111L;
+
+ ////////// Physical parameters:
+
+ min_rxtxtime = 4;
+ sib1_tda = 15;
+
+ servingCellConfigCommon = (
+ {
+ #spCellConfigCommon
+
+ physCellId = 1;
+
+# downlinkConfigCommon
+ #frequencyInfoDL
+ # this is 3600 MHz + 43 PRBs@30kHz SCS (same as initial BWP)
+ absoluteFrequencySSB = 435610;
+ dl_frequencyBand = 66;
+ # this is 3600 MHz
+ dl_absoluteFrequencyPointA = 435000;
+ #scs-SpecificCarrierList
+ dl_offstToCarrier = 0;
+# subcarrierSpacing
+# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
+ dl_subcarrierSpacing = 0;
+ dl_carrierBandwidth = 25;
+ #initialDownlinkBWP
+ #genericParameters
+ # this is RBstart=27,L=48 (275*(L-1))+RBstart
+ initialDLBWPlocationAndBandwidth = 6600; # 6366 12925 12956 28875 12952
+# subcarrierSpacing
+# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
+ initialDLBWPsubcarrierSpacing = 0;
+ #pdcch-ConfigCommon
+ initialDLBWPcontrolResourceSetZero = 0;
+ initialDLBWPsearchSpaceZero = 0;
+
+ #uplinkConfigCommon
+ #frequencyInfoUL
+ ul_frequencyBand = 66;
+ # this is 1750 MHz
+ ul_absoluteFrequencyPointA = 355000;
+ #scs-SpecificCarrierList
+ ul_offstToCarrier = 0;
+# subcarrierSpacing
+# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
+ ul_subcarrierSpacing = 0;
+ ul_carrierBandwidth = 25;
+ pMax = 20;
+ #initialUplinkBWP
+ #genericParameters
+ initialULBWPlocationAndBandwidth = 6600;
+# subcarrierSpacing
+# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
+ initialULBWPsubcarrierSpacing = 0;
+ #rach-ConfigCommon
+ #rach-ConfigGeneric
+ prach_ConfigurationIndex = 98;
+#prach_msg1_FDM
+#0 = one, 1=two, 2=four, 3=eight
+ prach_msg1_FDM = 0;
+ prach_msg1_FrequencyStart = 0;
+ zeroCorrelationZoneConfig = 13;
+ preambleReceivedTargetPower = -96;
+#preamblTransMax (0...10) = (3,4,5,6,7,8,10,20,50,100,200)
+ preambleTransMax = 6;
+#powerRampingStep
+# 0=dB0,1=dB2,2=dB4,3=dB6
+ powerRampingStep = 1;
+#ra_ReponseWindow
+#1,2,4,8,10,20,40,80
+ ra_ResponseWindow = 4;
+#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
+#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
+ ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;
+#oneHalf (0..15) 4,8,12,16,...60,64
+ ssb_perRACH_OccasionAndCB_PreamblesPerSSB = 15;
+#ra_ContentionResolutionTimer
+#(0..7) 8,16,24,32,40,48,56,64
+ ra_ContentionResolutionTimer = 7;
+ rsrp_ThresholdSSB = 19;
+#prach-RootSequenceIndex_PR
+#1 = 839, 2 = 139
+ prach_RootSequenceIndex_PR = 2;
+ prach_RootSequenceIndex = 1;
+ # SCS for msg1, can only be 15 for 30 kHz < 6 GHz, takes precendence over the one derived from prach-ConfigIndex
+ #
+ msg1_SubcarrierSpacing = 0,
+# restrictedSetConfig
+# 0=unrestricted, 1=restricted type A, 2=restricted type B
+ restrictedSetConfig = 0,
+
+ msg3_DeltaPreamble = 1;
+ p0_NominalWithGrant =-90;
+
+# pucch-ConfigCommon setup :
+# pucchGroupHopping
+# 0 = neither, 1= group hopping, 2=sequence hopping
+ pucchGroupHopping = 0;
+ hoppingId = 40;
+ p0_nominal = -90;
+
+ ssb_PositionsInBurst_Bitmap = 1;
+
+# ssb_periodicityServingCell
+# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1
+ ssb_periodicityServingCell = 2;
+
+# dmrs_TypeA_position
+# 0 = pos2, 1 = pos3
+ dmrs_TypeA_Position = 0;
+
+# subcarrierSpacing
+# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
+ subcarrierSpacing = 0;
+
+
+ #tdd-UL-DL-ConfigurationCommon
+# subcarrierSpacing
+# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
+ referenceSubcarrierSpacing = 0;
+ # pattern1
+ # dl_UL_TransmissionPeriodicity
+ # 0=ms0p5, 1=ms0p625, 2=ms1, 3=ms1p25, 4=ms2, 5=ms2p5, 6=ms5, 7=ms10
+ dl_UL_TransmissionPeriodicity = 6;
+ nrofDownlinkSlots = 7;
+ nrofDownlinkSymbols = 6;
+ nrofUplinkSlots = 2;
+ nrofUplinkSymbols = 4;
+
+ ssPBCH_BlockPower = -25;
+ }
+
+ );
+
+ # ------- SCTP definitions
+ SCTP :
+ {
+ # Number of streams to use in input/output
+ SCTP_INSTREAMS = 2;
+ SCTP_OUTSTREAMS = 2;
+ };
+ }
+);
+
+MACRLCs = ({
+ num_cc = 1;
+ tr_s_preference = "local_L1";
+ tr_n_preference = "f1";
+ local_n_if_name = "lo";
+ local_n_address = "127.0.0.5";
+ remote_n_address = "127.0.0.3";
+ local_n_portc = 500;
+ local_n_portd = 2152;
+ remote_n_portc = 501;
+ remote_n_portd = 2152;
+ pusch_FailureThres = 1000;
+});
+
+L1s = ({
+ num_cc = 1;
+ tr_n_preference = "local_mac";
+ prach_dtx_threshold = 200;
+ pucch0_dtx_threshold = 150;
+ ofdm_offset_divisor = 8; #set this to UINT_MAX for offset 0
+});
+
+RUs = ({
+ local_rf = "yes"
+ nb_tx = 1
+ nb_rx = 1
+ att_tx = 0
+ att_rx = 0;
+ bands = [66];
+ max_pdschReferenceSignalPower = -27;
+ max_rxgain = 114;
+ eNB_instances = [0];
+ clock_src = "internal";
+});
+
+rfsimulator: {
+ serveraddr = "server";
+ serverport = 4043;
+ options = (); #("saviq"); or/and "chanmod"
+ modelname = "AWGN";
+ IQfile = "/tmp/rfsimulator.iqs"
+}
+
+log_config: {
+ global_log_level = "info";
+ hw_log_level = "info";
+ nr_phy_log_level = "info";
+ nr_mac_log_level = "info";
+ rlc_log_level = "info";
+ pdcp_log_level = "info";
+ rrc_log_level = "info";
+ f1ap_log_level = "info";
+ ngap_log_level = "debug";
+};
diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band78.106prb.rfsim.pci0.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band78.106prb.rfsim.pci0.conf
new file mode 100644
index 0000000000..2e0f721504
--- /dev/null
+++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band78.106prb.rfsim.pci0.conf
@@ -0,0 +1,212 @@
+Active_gNBs = ( "du-rfsim");
+# Asn1_verbosity, choice in: none, info, annoying
+Asn1_verbosity = "none";
+
+gNBs =
+(
+ {
+ ////////// Identification parameters:
+ gNB_ID = 0xe00;
+ gNB_DU_ID = 0xe00;
+ gNB_name = "du-rfsim";
+
+ // Tracking area code, 0x0000 and 0xfffe are reserved values
+ tracking_area_code = 1;
+ plmn_list = ({ mcc = 001; mnc = 01; mnc_length = 2; snssaiList = ({ sst = 1; }) });
+
+ nr_cellid = 12345678L;
+
+ ////////// Physical parameters:
+
+ min_rxtxtime = 4;
+
+ servingCellConfigCommon = (
+ {
+ #spCellConfigCommon
+
+ physCellId = 0;
+
+# downlinkConfigCommon
+ #frequencyInfoDL
+ # this is 3600 MHz + 43 PRBs@30kHz SCS (same as initial BWP)
+ absoluteFrequencySSB = 630048;
+ dl_frequencyBand = 78;
+ # this is 3600 MHz
+ dl_absoluteFrequencyPointA = 628776;
+ #scs-SpecificCarrierList
+ dl_offstToCarrier = 0;
+# subcarrierSpacing
+# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
+ dl_subcarrierSpacing = 1;
+ dl_carrierBandwidth = 106;
+ #initialDownlinkBWP
+ #genericParameters
+ # this is RBstart=27,L=48 (275*(L-1))+RBstart
+ initialDLBWPlocationAndBandwidth = 28875; # 6366 12925 12956 28875 12952
+# subcarrierSpacing
+# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
+ initialDLBWPsubcarrierSpacing = 1;
+ #pdcch-ConfigCommon
+ initialDLBWPcontrolResourceSetZero = 12;
+ initialDLBWPsearchSpaceZero = 0;
+
+ #uplinkConfigCommon
+ #frequencyInfoUL
+ ul_frequencyBand = 78;
+ #scs-SpecificCarrierList
+ ul_offstToCarrier = 0;
+# subcarrierSpacing
+# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
+ ul_subcarrierSpacing = 1;
+ ul_carrierBandwidth = 106;
+ pMax = 20;
+ #initialUplinkBWP
+ #genericParameters
+ initialULBWPlocationAndBandwidth = 28875;
+# subcarrierSpacing
+# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
+ initialULBWPsubcarrierSpacing = 1;
+ #rach-ConfigCommon
+ #rach-ConfigGeneric
+ prach_ConfigurationIndex = 98;
+#prach_msg1_FDM
+#0 = one, 1=two, 2=four, 3=eight
+ prach_msg1_FDM = 0;
+ prach_msg1_FrequencyStart = 0;
+ zeroCorrelationZoneConfig = 13;
+ preambleReceivedTargetPower = -96;
+#preamblTransMax (0...10) = (3,4,5,6,7,8,10,20,50,100,200)
+ preambleTransMax = 6;
+#powerRampingStep
+# 0=dB0,1=dB2,2=dB4,3=dB6
+ powerRampingStep = 1;
+#ra_ReponseWindow
+#1,2,4,8,10,20,40,80
+ ra_ResponseWindow = 4;
+#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
+#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
+ ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;
+#oneHalf (0..15) 4,8,12,16,...60,64
+ ssb_perRACH_OccasionAndCB_PreamblesPerSSB = 14;
+#ra_ContentionResolutionTimer
+#(0..7) 8,16,24,32,40,48,56,64
+ ra_ContentionResolutionTimer = 7;
+ rsrp_ThresholdSSB = 19;
+#prach-RootSequenceIndex_PR
+#1 = 839, 2 = 139
+ prach_RootSequenceIndex_PR = 2;
+ prach_RootSequenceIndex = 1;
+ # SCS for msg1, can only be 15 for 30 kHz < 6 GHz, takes precendence over the one derived from prach-ConfigIndex
+ #
+ msg1_SubcarrierSpacing = 1,
+# restrictedSetConfig
+# 0=unrestricted, 1=restricted type A, 2=restricted type B
+ restrictedSetConfig = 0,
+
+ msg3_DeltaPreamble = 1;
+ p0_NominalWithGrant =-90;
+
+# pucch-ConfigCommon setup :
+# pucchGroupHopping
+# 0 = neither, 1= group hopping, 2=sequence hopping
+ pucchGroupHopping = 0;
+ hoppingId = 40;
+ p0_nominal = -90;
+
+ ssb_PositionsInBurst_Bitmap = 1;
+
+# ssb_periodicityServingCell
+# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1
+ ssb_periodicityServingCell = 2;
+
+# dmrs_TypeA_position
+# 0 = pos2, 1 = pos3
+ dmrs_TypeA_Position = 0;
+
+# subcarrierSpacing
+# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
+ subcarrierSpacing = 1;
+
+
+ #tdd-UL-DL-ConfigurationCommon
+# subcarrierSpacing
+# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
+ referenceSubcarrierSpacing = 1;
+ # pattern1
+ # dl_UL_TransmissionPeriodicity
+ # 0=ms0p5, 1=ms0p625, 2=ms1, 3=ms1p25, 4=ms2, 5=ms2p5, 6=ms5, 7=ms10
+ dl_UL_TransmissionPeriodicity = 6;
+ nrofDownlinkSlots = 7;
+ nrofDownlinkSymbols = 6;
+ nrofUplinkSlots = 2;
+ nrofUplinkSymbols = 4;
+
+ ssPBCH_BlockPower = -25;
+ }
+
+ );
+
+ # ------- SCTP definitions
+ SCTP :
+ {
+ # Number of streams to use in input/output
+ SCTP_INSTREAMS = 2;
+ SCTP_OUTSTREAMS = 2;
+ };
+ }
+);
+
+MACRLCs = ({
+ num_cc = 1;
+ tr_s_preference = "local_L1";
+ tr_n_preference = "f1";
+ local_n_if_name = "lo";
+ local_n_address = "127.0.0.4";
+ remote_n_address = "127.0.0.3";
+ local_n_portc = 500;
+ local_n_portd = 2152;
+ remote_n_portc = 501;
+ remote_n_portd = 2152;
+ pusch_FailureThres = 1000;
+});
+
+L1s = ({
+ num_cc = 1;
+ tr_n_preference = "local_mac";
+ prach_dtx_threshold = 200;
+ pucch0_dtx_threshold = 150;
+ ofdm_offset_divisor = 8; #set this to UINT_MAX for offset 0
+});
+
+RUs = ({
+ local_rf = "yes"
+ nb_tx = 1
+ nb_rx = 1
+ att_tx = 0
+ att_rx = 0;
+ bands = [78];
+ max_pdschReferenceSignalPower = -27;
+ max_rxgain = 114;
+ eNB_instances = [0];
+ clock_src = "internal";
+});
+
+rfsimulator: {
+ serveraddr = "server";
+ serverport = 4043;
+ options = (); #("saviq"); or/and "chanmod"
+ modelname = "AWGN";
+ IQfile = "/tmp/rfsimulator.iqs"
+}
+
+log_config: {
+ global_log_level = "info";
+ hw_log_level = "info";
+ nr_phy_log_level = "info";
+ nr_mac_log_level = "info";
+ rlc_log_level = "info";
+ pdcp_log_level = "info";
+ rrc_log_level = "info";
+ f1ap_log_level = "info";
+ ngap_log_level = "debug";
+};
diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band78.106prb.rfsim.pci1.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band78.106prb.rfsim.pci1.conf
new file mode 100644
index 0000000000..8e939887ce
--- /dev/null
+++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band78.106prb.rfsim.pci1.conf
@@ -0,0 +1,212 @@
+Active_gNBs = ( "du-rfsim");
+# Asn1_verbosity, choice in: none, info, annoying
+Asn1_verbosity = "none";
+
+gNBs =
+(
+ {
+ ////////// Identification parameters:
+ gNB_ID = 0xe00;
+ gNB_DU_ID = 0xe01;
+ gNB_name = "du-rfsim";
+
+ // Tracking area code, 0x0000 and 0xfffe are reserved values
+ tracking_area_code = 1;
+ plmn_list = ({ mcc = 001; mnc = 01; mnc_length = 2; snssaiList = ({ sst = 1; }) });
+
+ nr_cellid = 11111111L;
+
+ ////////// Physical parameters:
+
+ min_rxtxtime = 4;
+
+ servingCellConfigCommon = (
+ {
+ #spCellConfigCommon
+
+ physCellId = 1;
+
+# downlinkConfigCommon
+ #frequencyInfoDL
+ # this is 3600 MHz + 43 PRBs@30kHz SCS (same as initial BWP)
+ absoluteFrequencySSB = 643296;
+ dl_frequencyBand = 78;
+ # this is 3600 MHz
+ dl_absoluteFrequencyPointA = 642024;
+ #scs-SpecificCarrierList
+ dl_offstToCarrier = 0;
+# subcarrierSpacing
+# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
+ dl_subcarrierSpacing = 1;
+ dl_carrierBandwidth = 106;
+ #initialDownlinkBWP
+ #genericParameters
+ # this is RBstart=27,L=48 (275*(L-1))+RBstart
+ initialDLBWPlocationAndBandwidth = 28875; # 6366 12925 12956 28875 12952
+# subcarrierSpacing
+# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
+ initialDLBWPsubcarrierSpacing = 1;
+ #pdcch-ConfigCommon
+ initialDLBWPcontrolResourceSetZero = 12;
+ initialDLBWPsearchSpaceZero = 0;
+
+ #uplinkConfigCommon
+ #frequencyInfoUL
+ ul_frequencyBand = 78;
+ #scs-SpecificCarrierList
+ ul_offstToCarrier = 0;
+# subcarrierSpacing
+# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
+ ul_subcarrierSpacing = 1;
+ ul_carrierBandwidth = 106;
+ pMax = 20;
+ #initialUplinkBWP
+ #genericParameters
+ initialULBWPlocationAndBandwidth = 28875;
+# subcarrierSpacing
+# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
+ initialULBWPsubcarrierSpacing = 1;
+ #rach-ConfigCommon
+ #rach-ConfigGeneric
+ prach_ConfigurationIndex = 98;
+#prach_msg1_FDM
+#0 = one, 1=two, 2=four, 3=eight
+ prach_msg1_FDM = 0;
+ prach_msg1_FrequencyStart = 0;
+ zeroCorrelationZoneConfig = 13;
+ preambleReceivedTargetPower = -96;
+#preamblTransMax (0...10) = (3,4,5,6,7,8,10,20,50,100,200)
+ preambleTransMax = 6;
+#powerRampingStep
+# 0=dB0,1=dB2,2=dB4,3=dB6
+ powerRampingStep = 1;
+#ra_ReponseWindow
+#1,2,4,8,10,20,40,80
+ ra_ResponseWindow = 4;
+#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
+#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
+ ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;
+#oneHalf (0..15) 4,8,12,16,...60,64
+ ssb_perRACH_OccasionAndCB_PreamblesPerSSB = 14;
+#ra_ContentionResolutionTimer
+#(0..7) 8,16,24,32,40,48,56,64
+ ra_ContentionResolutionTimer = 7;
+ rsrp_ThresholdSSB = 19;
+#prach-RootSequenceIndex_PR
+#1 = 839, 2 = 139
+ prach_RootSequenceIndex_PR = 2;
+ prach_RootSequenceIndex = 1;
+ # SCS for msg1, can only be 15 for 30 kHz < 6 GHz, takes precendence over the one derived from prach-ConfigIndex
+ #
+ msg1_SubcarrierSpacing = 1,
+# restrictedSetConfig
+# 0=unrestricted, 1=restricted type A, 2=restricted type B
+ restrictedSetConfig = 0,
+
+ msg3_DeltaPreamble = 1;
+ p0_NominalWithGrant =-90;
+
+# pucch-ConfigCommon setup :
+# pucchGroupHopping
+# 0 = neither, 1= group hopping, 2=sequence hopping
+ pucchGroupHopping = 0;
+ hoppingId = 40;
+ p0_nominal = -90;
+
+ ssb_PositionsInBurst_Bitmap = 3;
+
+# ssb_periodicityServingCell
+# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1
+ ssb_periodicityServingCell = 2;
+
+# dmrs_TypeA_position
+# 0 = pos2, 1 = pos3
+ dmrs_TypeA_Position = 0;
+
+# subcarrierSpacing
+# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
+ subcarrierSpacing = 1;
+
+
+ #tdd-UL-DL-ConfigurationCommon
+# subcarrierSpacing
+# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
+ referenceSubcarrierSpacing = 1;
+ # pattern1
+ # dl_UL_TransmissionPeriodicity
+ # 0=ms0p5, 1=ms0p625, 2=ms1, 3=ms1p25, 4=ms2, 5=ms2p5, 6=ms5, 7=ms10
+ dl_UL_TransmissionPeriodicity = 6;
+ nrofDownlinkSlots = 7;
+ nrofDownlinkSymbols = 6;
+ nrofUplinkSlots = 2;
+ nrofUplinkSymbols = 4;
+
+ ssPBCH_BlockPower = -25;
+ }
+
+ );
+
+ # ------- SCTP definitions
+ SCTP :
+ {
+ # Number of streams to use in input/output
+ SCTP_INSTREAMS = 2;
+ SCTP_OUTSTREAMS = 2;
+ };
+ }
+);
+
+MACRLCs = ({
+ num_cc = 1;
+ tr_s_preference = "local_L1";
+ tr_n_preference = "f1";
+ local_n_if_name = "lo";
+ local_n_address = "127.0.0.5";
+ remote_n_address = "127.0.0.3";
+ local_n_portc = 500;
+ local_n_portd = 2152;
+ remote_n_portc = 501;
+ remote_n_portd = 2152;
+ pusch_FailureThres = 1000;
+});
+
+L1s = ({
+ num_cc = 1;
+ tr_n_preference = "local_mac";
+ prach_dtx_threshold = 200;
+ pucch0_dtx_threshold = 150;
+ ofdm_offset_divisor = 8; #set this to UINT_MAX for offset 0
+});
+
+RUs = ({
+ local_rf = "yes"
+ nb_tx = 1
+ nb_rx = 1
+ att_tx = 0
+ att_rx = 0;
+ bands = [78];
+ max_pdschReferenceSignalPower = -27;
+ max_rxgain = 114;
+ eNB_instances = [0];
+ clock_src = "internal";
+});
+
+rfsimulator: {
+ serveraddr = "server";
+ serverport = 4043;
+ options = (); #("saviq"); or/and "chanmod"
+ modelname = "AWGN";
+ IQfile = "/tmp/rfsimulator.iqs"
+}
+
+log_config: {
+ global_log_level = "info";
+ hw_log_level = "info";
+ nr_phy_log_level = "info";
+ nr_mac_log_level = "info";
+ rlc_log_level = "info";
+ pdcp_log_level = "info";
+ rrc_log_level = "info";
+ f1ap_log_level = "info";
+ ngap_log_level = "debug";
+};
diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.106PRB.usrpb210.RA_2-Step.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.106PRB.usrpb210.RA_2-Step.conf
index 45dbf2197f..d59dca9384 100644
--- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.106PRB.usrpb210.RA_2-Step.conf
+++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.106PRB.usrpb210.RA_2-Step.conf
@@ -226,8 +226,6 @@ RUs = (
max_pdschReferenceSignalPower = -27;
max_rxgain = 114;
eNB_instances = [0];
- #beamforming 1x4 matrix:
- bf_weights = [0x00007fff, 0x0000, 0x0000, 0x0000];
clock_src = "internal";
}
);