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. + +![F1 Handover setup](./RRC/ho.png) + +# 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"; } );