mirror of
https://gitlab.eurecom.fr/oai/openairinterface5g.git
synced 2026-07-15 13:40:29 +00:00
Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bf91b249fb | ||
|
|
8c11c7ee7b | ||
|
|
5518b4f7a9 | ||
|
|
2d574c9475 | ||
|
|
3850acf45c | ||
|
|
62ad613ac4 | ||
|
|
c7e4940f81 | ||
|
|
4d165cb42f | ||
|
|
fe8d85008a | ||
|
|
d1dae2afa7 | ||
|
|
2f73d98712 | ||
|
|
ab653d3335 | ||
|
|
79ae76fec5 | ||
|
|
c6ce348337 | ||
|
|
35260c5299 | ||
|
|
59e24df638 | ||
|
|
12ca43049a | ||
|
|
cab6adbe94 |
@@ -398,7 +398,9 @@ target_link_libraries(lte_rrc PRIVATE nr_rrc)
|
||||
|
||||
#NR RRC
|
||||
#######
|
||||
add_library(nr_rrc ${OPENAIR2_DIR}/RRC/NR/MESSAGES/asn1_msg.c)
|
||||
add_library(nr_rrc
|
||||
${OPENAIR2_DIR}/RRC/NR/MESSAGES/asn1_msg.c
|
||||
${OPENAIR2_DIR}/RRC/NR/rrc_gNB_measurements.c)
|
||||
target_link_libraries(nr_rrc PUBLIC asn1_nr_rrc asn1_lte_rrc rrc_cell_management)
|
||||
|
||||
# S1AP
|
||||
@@ -1134,6 +1136,7 @@ set(L2_NR_SRC
|
||||
${NR_RRC_DIR}/rrc_gNB_cuup.c
|
||||
${NR_RRC_DIR}/rrc_gNB_du.c
|
||||
${NR_RRC_DIR}/rrc_gNB_mobility.c
|
||||
${NR_RRC_DIR}/rrc_gNB_nrdc.c
|
||||
)
|
||||
|
||||
set(L2_SRC_UE
|
||||
@@ -1211,6 +1214,7 @@ set (MAC_NR_SRC
|
||||
${NR_GNB_MAC_DIR}/mac_rrc_ul_direct.c
|
||||
${NR_GNB_MAC_DIR}/mac_rrc_ul_f1ap.c
|
||||
${NR_GNB_MAC_DIR}/nr_radio_config.c
|
||||
${NR_GNB_MAC_DIR}/nrdc_mac_rrc_dl_handler.c
|
||||
)
|
||||
|
||||
|
||||
@@ -1784,7 +1788,7 @@ target_link_libraries(nr-oru PRIVATE
|
||||
radio_common softmodem_common nfapi_pnf_lib)
|
||||
target_link_libraries(nr-oru PRIVATE pthread m CONFIG_LIB rt ${T_LIB} utils
|
||||
barrier actor nfapi_user_lib)
|
||||
target_link_libraries(nr-oru PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs nr_phy_common time_management)
|
||||
target_link_libraries(nr-oru PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs nr_phy_common time_management ds)
|
||||
|
||||
# nr-softmodem
|
||||
###################################################
|
||||
@@ -1987,7 +1991,7 @@ add_executable(nr_dlsim
|
||||
${PHY_INTERFACE_DIR}/queue_t.c
|
||||
)
|
||||
target_link_libraries(nr_dlsim PRIVATE
|
||||
-Wl,--start-group UTIL SIMU PHY_NR_COMMON PHY_NR PHY_NR_UE SCHED_NR_LIB SCHED_NR_UE_LIB MAC_UE_NR MAC_NR_COMMON CONFIG_LIB L2_NR -Wl,--end-group
|
||||
-Wl,--start-group UTIL SIMU PHY_NR_COMMON PHY_NR PHY_NR_UE SCHED_NR_LIB SCHED_NR_UE_LIB MAC_UE_NR MAC_NR_COMMON CONFIG_LIB L2_NR time_management_core -Wl,--end-group
|
||||
m pthread ${T_LIB} ITTI dl nr_ue_phy_meas physim_common NR_L2_UE
|
||||
softmodem_common
|
||||
)
|
||||
@@ -2016,7 +2020,7 @@ add_executable(nr_ulsim
|
||||
)
|
||||
|
||||
target_link_libraries(nr_ulsim PRIVATE
|
||||
-Wl,--start-group UTIL SIMU PHY_NR_COMMON PHY_NR PHY_NR_UE SCHED_NR_LIB SCHED_NR_UE_LIB MAC_UE_NR MAC_NR_COMMON CONFIG_LIB L2_NR -Wl,--end-group
|
||||
-Wl,--start-group UTIL SIMU PHY_NR_COMMON PHY_NR PHY_NR_UE SCHED_NR_LIB SCHED_NR_UE_LIB MAC_UE_NR MAC_NR_COMMON CONFIG_LIB L2_NR time_management_core -Wl,--end-group
|
||||
m pthread ${T_LIB} ITTI dl nr_ue_phy_meas physim_common softmodem_common NR_L2_UE
|
||||
)
|
||||
target_link_libraries(nr_ulsim PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs)
|
||||
|
||||
@@ -7,6 +7,7 @@ add_library(ds OBJECT
|
||||
hashtable.c
|
||||
obj_hashtable.c
|
||||
spsc_q.c
|
||||
shared_pointer.c
|
||||
)
|
||||
|
||||
target_include_directories(ds PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
56
common/utils/ds/shared_pointer.c
Normal file
56
common/utils/ds/shared_pointer.c
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
|
||||
*/
|
||||
|
||||
#include "shared_pointer.h"
|
||||
#include "common/utils/utils.h"
|
||||
|
||||
#define fatal(x, ...) do { \
|
||||
if (!(x)) { \
|
||||
fprintf(stderr, __VA_ARGS__); \
|
||||
fflush(stderr); \
|
||||
exit(1); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static void lock(shared_pointer_t *p)
|
||||
{
|
||||
fatal(pthread_mutex_lock(&p->m) == 0, "pthread_mutex_lock failed\n");
|
||||
}
|
||||
|
||||
static void unlock(shared_pointer_t *p)
|
||||
{
|
||||
fatal(pthread_mutex_unlock(&p->m) == 0, "pthread_mutex_unlock failed\n");
|
||||
}
|
||||
|
||||
shared_pointer_t *new_shared_pointer(void *p, void (*free)(void *p))
|
||||
{
|
||||
shared_pointer_t *s = malloc_or_fail(sizeof(*s));
|
||||
s->p = p;
|
||||
s->free = free;
|
||||
s->count = 1;
|
||||
pthread_mutex_init(&s->m, NULL);
|
||||
return s;
|
||||
}
|
||||
|
||||
void unref_shared_pointer(shared_pointer_t *p)
|
||||
{
|
||||
lock(p);
|
||||
fatal(p->count, "unref_shared_pointer() called for a pointer with count == 0\n");
|
||||
p->count--;
|
||||
if (p->count) {
|
||||
unlock(p);
|
||||
return;
|
||||
}
|
||||
unlock(p);
|
||||
p->free(p->p);
|
||||
free(p);
|
||||
}
|
||||
|
||||
void ref_shared_pointer(shared_pointer_t *p)
|
||||
{
|
||||
lock(p);
|
||||
fatal(p->count, "ref_shared_pointer() called for a pointer with count == 0\n");
|
||||
p->count++;
|
||||
unlock(p);
|
||||
}
|
||||
21
common/utils/ds/shared_pointer.h
Normal file
21
common/utils/ds/shared_pointer.h
Normal file
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
|
||||
*/
|
||||
|
||||
#ifndef COMMON_UTILS_DS_SHARED_POINTER_H
|
||||
#define COMMON_UTILS_DS_SHARED_POINTER_H
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
typedef struct {
|
||||
void *p;
|
||||
void (*free)(void *p);
|
||||
int count;
|
||||
pthread_mutex_t m;
|
||||
} shared_pointer_t;
|
||||
|
||||
shared_pointer_t *new_shared_pointer(void *p, void (*free)(void *p));
|
||||
void unref_shared_pointer(shared_pointer_t *p);
|
||||
void ref_shared_pointer(shared_pointer_t *p);
|
||||
|
||||
#endif /* COMMON_UTILS_DS_SHARED_POINTER_H */
|
||||
@@ -7,6 +7,16 @@
|
||||
#include "../../alg/foreach.h"
|
||||
#include <assert.h>
|
||||
|
||||
/* needed because shared_pointer.c is using DevAssert() and AssertFatal() */
|
||||
void exit_function(const char *file, const char *function, const int line, const char *s, const int assert)
|
||||
{
|
||||
if (assert) {
|
||||
abort();
|
||||
} else {
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Example to show seq_arr_t capabilities and usage
|
||||
To compile: gcc test_seq_array.c ../seq_arr.c ../../alg/find.c ../../alg/foreach.c
|
||||
|
||||
@@ -6,7 +6,8 @@ add_library(time_management STATIC
|
||||
add_library(time_management_core OBJECT
|
||||
time_source.c
|
||||
time_server.c
|
||||
time_client.c)
|
||||
time_client.c
|
||||
time_timer.c)
|
||||
|
||||
target_link_libraries(time_management PUBLIC time_management_core)
|
||||
target_link_libraries(time_management_core PRIVATE log_headers)
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
add_executable(test_time_manager_manual test_manual.c)
|
||||
target_link_libraries(test_time_manager_manual time_management_core)
|
||||
target_link_libraries(test_time_manager_manual LOG CONFIG_LIB)
|
||||
target_link_libraries(test_time_manager_manual LOG CONFIG_LIB ds)
|
||||
|
||||
add_executable(test_time_manager_auto test_auto.c)
|
||||
target_link_libraries(test_time_manager_auto time_management)
|
||||
target_link_libraries(test_time_manager_auto LOG CONFIG_LIB)
|
||||
target_link_libraries(test_time_manager_auto LOG CONFIG_LIB ds)
|
||||
|
||||
add_dependencies(tests test_time_manager_auto)
|
||||
add_test(NAME time_management_tests
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "time_source.h"
|
||||
#include "time_server.h"
|
||||
#include "time_client.h"
|
||||
#include "time_timer.h"
|
||||
|
||||
#include "common/config/config_userapi.h"
|
||||
#include "common/utils/assertions.h"
|
||||
@@ -27,6 +28,9 @@ static int tick_functions_count;
|
||||
static void tick(void *unused)
|
||||
{
|
||||
UNUSED(unused);
|
||||
|
||||
time_timer_ms_tick();
|
||||
|
||||
for (int i = 0; i < tick_functions_count; i++)
|
||||
tick_functions[i]();
|
||||
}
|
||||
@@ -74,6 +78,8 @@ void time_manager_start(time_manager_tick_function_t *_tick_functions,
|
||||
char *server_ip = NULL;
|
||||
int server_port = -1;
|
||||
|
||||
time_timer_init();
|
||||
|
||||
tick_functions_count = _tick_functions_count;
|
||||
tick_functions = calloc_or_fail(tick_functions_count, sizeof(*tick_functions));
|
||||
memcpy(tick_functions, _tick_functions, tick_functions_count * sizeof(*tick_functions));
|
||||
@@ -186,4 +192,6 @@ void time_manager_finish(void)
|
||||
free(tick_functions);
|
||||
tick_functions = NULL;
|
||||
tick_functions_count = 0;
|
||||
|
||||
time_timer_free();
|
||||
}
|
||||
|
||||
249
common/utils/time_manager/time_timer.c
Normal file
249
common/utils/time_manager/time_timer.c
Normal file
@@ -0,0 +1,249 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
|
||||
*/
|
||||
|
||||
#include "time_timer.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "common/utils/ds/shared_pointer.h"
|
||||
#include "common/utils/assertions.h"
|
||||
#include "common/utils/system.h"
|
||||
#include "common/utils/LOG/log.h"
|
||||
|
||||
#define MAX_DELAY_MS 32768 /* 32s max of timeout - change if more needed */
|
||||
|
||||
typedef struct {
|
||||
void (*callback)(void **p, uint64_t *v);
|
||||
void **p;
|
||||
uint64_t *v;
|
||||
_Atomic bool disabled;
|
||||
_Atomic bool callback_running;
|
||||
pthread_mutex_t m;
|
||||
pthread_cond_t c;
|
||||
shared_pointer_t *next; /* this is a shared_pointer_t(time_timer_item_t) */
|
||||
} time_timer_item_t;
|
||||
|
||||
static void lock_item(time_timer_item_t *t)
|
||||
{
|
||||
DevAssert(pthread_mutex_lock(&t->m) == 0);
|
||||
}
|
||||
|
||||
static void unlock_item(time_timer_item_t *t)
|
||||
{
|
||||
DevAssert(pthread_mutex_unlock(&t->m) == 0);
|
||||
}
|
||||
|
||||
static void do_wait_item(time_timer_item_t *t)
|
||||
{
|
||||
DevAssert(pthread_cond_wait(&t->c, &t->m) == 0);
|
||||
}
|
||||
|
||||
static void do_signal_item(time_timer_item_t *t)
|
||||
{
|
||||
DevAssert(pthread_cond_signal(&t->c) == 0);
|
||||
}
|
||||
|
||||
static void free_item(void *_item)
|
||||
{
|
||||
time_timer_item_t *item = _item;
|
||||
free(item->p);
|
||||
free(item->v);
|
||||
free(item);
|
||||
}
|
||||
|
||||
/* a list of shared_pointer_t(time_timer_item_t) */
|
||||
typedef struct {
|
||||
shared_pointer_t *head;
|
||||
shared_pointer_t *tail;
|
||||
} time_timer_list_t;
|
||||
|
||||
static void list_add(time_timer_list_t *l, shared_pointer_t *p)
|
||||
{
|
||||
if (!l->head) {
|
||||
l->head = p;
|
||||
l->tail = p;
|
||||
} else {
|
||||
time_timer_item_t *last = l->tail->p;
|
||||
last->next = p;
|
||||
l->tail = p;
|
||||
}
|
||||
}
|
||||
|
||||
/* a timer module */
|
||||
typedef struct {
|
||||
pthread_t thread_id;
|
||||
pthread_mutex_t m;
|
||||
pthread_cond_t c;
|
||||
uint64_t cur_time;
|
||||
bool exit;
|
||||
time_timer_list_t timers[MAX_DELAY_MS];
|
||||
} time_timer_t;
|
||||
|
||||
static void lock(time_timer_t *t)
|
||||
{
|
||||
DevAssert(pthread_mutex_lock(&t->m) == 0);
|
||||
}
|
||||
|
||||
static void unlock(time_timer_t *t)
|
||||
{
|
||||
DevAssert(pthread_mutex_unlock(&t->m) == 0);
|
||||
}
|
||||
|
||||
static void do_wait(time_timer_t *t)
|
||||
{
|
||||
DevAssert(pthread_cond_wait(&t->c, &t->m) == 0);
|
||||
}
|
||||
|
||||
static void do_signal(time_timer_t *t)
|
||||
{
|
||||
DevAssert(pthread_cond_signal(&t->c) == 0);
|
||||
}
|
||||
|
||||
static void *time_timer_thread(void *_t)
|
||||
{
|
||||
time_timer_t *t = _t;
|
||||
int cur_pos = 0;
|
||||
uint64_t cur_time = 0;
|
||||
|
||||
while (1) {
|
||||
lock(t);
|
||||
while (!(t->exit || cur_time != t->cur_time))
|
||||
do_wait(t);
|
||||
if (t->exit) {
|
||||
unlock(t);
|
||||
break;
|
||||
}
|
||||
uint64_t next_time = t->cur_time;
|
||||
unlock(t);
|
||||
|
||||
/* process all ticks up to t->cur_time */
|
||||
while (cur_time != next_time) {
|
||||
lock(t);
|
||||
shared_pointer_t *l = t->timers[cur_pos].head;
|
||||
t->timers[cur_pos].head = 0;
|
||||
t->timers[cur_pos].tail = 0;
|
||||
unlock(t);
|
||||
while (l) {
|
||||
time_timer_item_t *p = l->p;
|
||||
shared_pointer_t *next = p->next;
|
||||
lock_item(p);
|
||||
if (!p->disabled) {
|
||||
p->callback_running = true;
|
||||
unlock_item(p);
|
||||
p->callback(p->p, p->v);
|
||||
lock_item(p);
|
||||
p->callback_running = false;
|
||||
do_signal_item(p);
|
||||
}
|
||||
unlock_item(p);
|
||||
unref_shared_pointer(l);
|
||||
l = next;
|
||||
}
|
||||
cur_pos++;
|
||||
cur_pos %= MAX_DELAY_MS;
|
||||
cur_time++;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* global timer module API */
|
||||
static time_timer_t timer_module;
|
||||
|
||||
void time_timer_init(void)
|
||||
{
|
||||
pthread_mutex_init(&timer_module.m, 0);
|
||||
pthread_cond_init(&timer_module.c, 0);
|
||||
threadCreate(&timer_module.thread_id, time_timer_thread, &timer_module, "timer thread", -1, SCHED_OAI);
|
||||
}
|
||||
|
||||
void time_timer_free(void)
|
||||
{
|
||||
lock(&timer_module);
|
||||
timer_module.exit = true;
|
||||
do_signal(&timer_module);
|
||||
unlock(&timer_module);
|
||||
|
||||
void *retval;
|
||||
int ret = pthread_join(timer_module.thread_id, &retval);
|
||||
if (ret) LOG_E(UTIL, "pthread_join failed for the time_timer module (%s)\n", strerror(ret));
|
||||
|
||||
lock(&timer_module);
|
||||
for (int i = 0; i < MAX_DELAY_MS; i++) {
|
||||
time_timer_list_t *l = &timer_module.timers[i];
|
||||
shared_pointer_t *cur = l->head;
|
||||
while (cur) {
|
||||
time_timer_item_t *p = cur->p;
|
||||
shared_pointer_t *next = p->next;
|
||||
unref_shared_pointer(cur);
|
||||
cur = next;
|
||||
}
|
||||
l->head = 0;
|
||||
l->tail = 0;
|
||||
}
|
||||
unlock(&timer_module);
|
||||
}
|
||||
|
||||
void time_timer_ms_tick(void)
|
||||
{
|
||||
lock(&timer_module);
|
||||
timer_module.cur_time++;
|
||||
do_signal(&timer_module);
|
||||
unlock(&timer_module);
|
||||
}
|
||||
|
||||
/* start/stop a timer */
|
||||
|
||||
void *tick_timeout_start(int ms, void (*callback)(void **p, uint64_t *v), void **p, int psize, uint64_t *v, int vsize)
|
||||
{
|
||||
AssertFatal(ms < MAX_DELAY_MS - 2, "cannot create timer with timeout %d ms, increase MAX_DELAY_MS in the code and recompile\n", ms);
|
||||
|
||||
/* create new timer object */
|
||||
time_timer_item_t *new_timer = malloc_or_fail(sizeof(*new_timer));
|
||||
new_timer->callback = callback;
|
||||
new_timer->p = 0;
|
||||
new_timer->v = 0;
|
||||
if (psize) {
|
||||
new_timer->p = malloc_or_fail(sizeof(void *) * psize);
|
||||
memcpy(new_timer->p, p, sizeof(void *) * psize);
|
||||
}
|
||||
if (vsize) {
|
||||
new_timer->v = malloc_or_fail(sizeof(uint64_t) * vsize);
|
||||
memcpy(new_timer->v, v, sizeof(uint64_t *) * vsize);
|
||||
}
|
||||
new_timer->disabled = false;
|
||||
new_timer->callback_running = false;
|
||||
pthread_mutex_init(&new_timer->m, 0);
|
||||
pthread_cond_init(&new_timer->c, 0);
|
||||
new_timer->next = 0;
|
||||
|
||||
/* make a shared pointer around it */
|
||||
shared_pointer_t *shared_ptr = new_shared_pointer(new_timer, free_item);
|
||||
/* ref it, one local copy in the list, one to the caller, so 2 refs are needed */
|
||||
ref_shared_pointer(shared_ptr);
|
||||
|
||||
/* put it in the list */
|
||||
lock(&timer_module);
|
||||
int pos = (timer_module.cur_time + ms) % MAX_DELAY_MS;
|
||||
time_timer_list_t *list = &timer_module.timers[pos];
|
||||
list_add(list, shared_ptr);
|
||||
unlock(&timer_module);
|
||||
|
||||
return shared_ptr;
|
||||
}
|
||||
|
||||
void tick_timeout_stop(void *_timer)
|
||||
{
|
||||
shared_pointer_t *shared_ptr = _timer;
|
||||
time_timer_item_t *timer = shared_ptr->p;
|
||||
lock_item(timer);
|
||||
timer->disabled = true;
|
||||
/* to delete a timer, its callback must not be running */
|
||||
while (timer->callback_running)
|
||||
do_wait_item(timer);
|
||||
unlock_item(timer);
|
||||
unref_shared_pointer(shared_ptr);
|
||||
}
|
||||
22
common/utils/time_manager/time_timer.h
Normal file
22
common/utils/time_manager/time_timer.h
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
|
||||
*/
|
||||
|
||||
#ifndef COMMON_UTIL_TIME_MANAGER_TIME_TIMER
|
||||
#define COMMON_UTIL_TIME_MANAGER_TIME_TIMER
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void time_timer_init(void);
|
||||
void time_timer_free(void);
|
||||
|
||||
void time_timer_ms_tick(void);
|
||||
|
||||
/* returns a timer pointer to be passed to tick_timeout_stop()
|
||||
* p, psize, v, vsize are parameters for the callback (we have
|
||||
* a many parameters as the user needs)
|
||||
*/
|
||||
void *tick_timeout_start(int ms, void (*callback)(void **p, uint64_t *v), void **p, int psize, uint64_t *v, int vsize);
|
||||
void tick_timeout_stop(void *timer);
|
||||
|
||||
#endif /* COMMON_UTIL_TIME_MANAGER_TIME_TIMER */
|
||||
185
doc/nr-dc.md
Normal file
185
doc/nr-dc.md
Normal file
@@ -0,0 +1,185 @@
|
||||
# NR-DC tutorial
|
||||
|
||||
This tutorial explains how to test NR-DC with OAI gNB.
|
||||
|
||||
## F1 NR-DC
|
||||
|
||||
It is possible to test F1-based NR-DC with OAI gNB.
|
||||
|
||||
In F1-based NR-DC, the UE is connected to a unique CU (that handles
|
||||
upper layer protocols, PDCP and above and is connected to the core
|
||||
network). Its signalling radio bearers are using the FR1 DU for lower
|
||||
layers (RLC, MAC and PHY). Its data radio bearer (DRB) (in this scenario,
|
||||
we suppose that the UE requests only one bearer) is also using the FR1
|
||||
DU for lower layers when it is established but its lower layers (RLC
|
||||
and below) are immediately transferred to the FR2 DU.
|
||||
|
||||
### Setup
|
||||
|
||||
The setup is as follows:
|
||||
- one CU
|
||||
- one FR1 DU connected to the CU
|
||||
- one FR2 DU connected to the same CU
|
||||
|
||||
Both DUs need to be tightly synchronized. This can be achieved in different
|
||||
ways depending on the hardware used.
|
||||
|
||||
For O-RAN 7.2 RUs, ensure that you use the same PTP clock for both units.
|
||||
|
||||
For USRP-based setups (for example, the FR1 DU is using a B210 and the FR2
|
||||
DU is using an X410 connected to an Interdigital MHU) you need to set the
|
||||
following in the `Rus` section for both units:
|
||||
|
||||
```
|
||||
clock_src = "external";
|
||||
time_src = "external";
|
||||
```
|
||||
|
||||
And you need to connect both units to the same clock and time sources (PPS
|
||||
and 10 MHz), for example, a USRP OctoClock.
|
||||
|
||||
To set up the CU and the DUs, refer to the documents
|
||||
[F1AP/F1-design.md](F1AP/F1-design.md) and
|
||||
[handover-tutorial.md](handover-tutorial.md) that explain how to run one CU
|
||||
with two different DUs connected to it.
|
||||
|
||||
### UEs
|
||||
|
||||
NR-DC was successfully tested with the following UEs:
|
||||
|
||||
- Samsung S25 (FR1 band n77 40MHz and FR2 band n261 100MHz)
|
||||
- Quectel RG530 (FR1 band n77 40MHz and FR2 band n261 100MHz)
|
||||
|
||||
### Compilation
|
||||
|
||||
Refer to the standard ways, depending on your hardware setup.
|
||||
|
||||
### Configuration
|
||||
|
||||
Add the following in the CU configuration file (at the end,
|
||||
or anywhere but outside of other blocks).
|
||||
|
||||
```
|
||||
nrdc = {
|
||||
combinations = (
|
||||
{ mcg = 77, scg = 261 },
|
||||
{ mcg = 78, scg = 257 }
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
This will enable two different possible band combinations, n77+n261 and
|
||||
n78+n257.
|
||||
|
||||
If you run one DU in band n77 and another one in band n261, then a UE that
|
||||
supports this NR-DC combination will be put in NR-DC mode. The same applies
|
||||
if you run one DU in band n78 and another one in band n257.
|
||||
|
||||
### Testing
|
||||
|
||||
Once you have your three proper configuration files for the CU and the
|
||||
two DUs and the system is up and running, connect a UE to the FR1 band.
|
||||
And if the UE supports the configured band combination, its first PDU
|
||||
session will be transferred to the FR2 DU.
|
||||
|
||||
For example, when testing a Quectel RM530 with bands n77 and n261, run the
|
||||
following AT command so that it will connect only to band n77 and not
|
||||
attempt to connect to band n261, which it is otherwise capable of doing.
|
||||
|
||||
```
|
||||
AT+QNWPREFCFG="nr5g_band",77
|
||||
```
|
||||
|
||||
For the Samsung S25, you need to enable all bands and band combinations.
|
||||
Refer to this
|
||||
[external link](https://xdaforums.com/t/how-to-guide-how-to-enable-all-bands-through-service-menu-on-us-ca-s25-series-including-sub-6-and-mmwave.4768929).
|
||||
|
||||
### Limitations
|
||||
|
||||
Only one data radio bearer and one PDU session are supported at the moment.
|
||||
|
||||
Disable IMS if possible.
|
||||
|
||||
What actually happens is that only the first PDU session that is established
|
||||
will be transferred to the FR2 DU.
|
||||
|
||||
A multi-PDU session scenario was not tested.
|
||||
|
||||
### Traces
|
||||
|
||||
Some NR-DC traces may be found in the
|
||||
[wiki](https://gitlab.eurecom.fr/oai/openairinterface5g/-/wikis/nrdc/nrdc).
|
||||
|
||||
### Message flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant ue as UE
|
||||
participant dufr1 as DU FR1
|
||||
participant dufr2 as DU FR2
|
||||
participant cu as CU
|
||||
|
||||
note over cu,ue: initial connection, PDU session establishment
|
||||
|
||||
note over cu: CU decides to<br/>initiate NR-DC
|
||||
|
||||
cu ->> dufr1: DL RRC Message Transfer<br/>(UE Capability Enquiry)
|
||||
dufr1 ->> ue: 1. UE Capability Enquiry
|
||||
ue ->> dufr1: 2. UE Capability Information
|
||||
dufr1 ->> cu: UL RRC Message Transfer<br/>(UE Capability Information)
|
||||
|
||||
note over cu: CU checks the UE capabilities,<br/>finds a valid NR-DC combination
|
||||
|
||||
cu ->> dufr1: DL RRC Message Transfer<br/>(RRC Reconfiguration)
|
||||
dufr1 ->> ue: 3. RRC Reconfiguration<br/>(measConfig for FR2 DU)
|
||||
ue ->> dufr1: RRC Reconfiguration Complete
|
||||
dufr1 ->> cu: UL RRC Message Transfer<br/>(RRC Reconfiguration Complete)
|
||||
cu ->> dufr1: UE Context Modification Request<br/>(RRC Reconfiguration Complete Indicator)
|
||||
dufr1 ->> cu: UE Context Modification Response
|
||||
ue ->> dufr1: 4. Measurement Report
|
||||
dufr1 ->> cu: UL RRC Message Transfer<br/>(Measurement Report)
|
||||
cu ->> dufr2: 5. UE Context Setup Request
|
||||
cu ->> dufr1: 6. UE Context Modification Request
|
||||
dufr1 ->> cu: UE Context Modification Response
|
||||
dufr2 ->> cu: 7. UE Context Setup Response
|
||||
cu ->> dufr1: DL RRC Message Transfer<br/>(RRC Reconfiguration)
|
||||
dufr1 ->> ue: 8. RRC Reconfiguration<br/>(activate NR-DC)
|
||||
ue ->> dufr1: RRC Reconfiguration Complete
|
||||
dufr1 ->> cu: UL RRC Message Transfer<br/>(RRC Reconfiguration Complete)
|
||||
cu ->> dufr1: UE Context Modification Request<br/>(RRC Reconfiguration Complete Indicator)
|
||||
dufr1 ->> cu: UE Context Modification Response
|
||||
ue ->> dufr2: 9. Random Access<br/>(using provided CFRA configuration)
|
||||
dufr2 ->> ue: 10. RAR
|
||||
|
||||
note over cu,ue: the PDU session is now using FR2 DU
|
||||
```
|
||||
|
||||
1. The UE Capability Enquiry contains the `includeNR-DC` field and a
|
||||
`CellGrouping` to specify the MCG and SCG bands
|
||||
2. The UE Capability Information contains the supported Band Combination List
|
||||
that is checked by the CU to activate NR-DC
|
||||
3. The RRC Reconfiguration contains a measurement configuration for the
|
||||
FR2 DU.
|
||||
4. The Measurement Report contains a measurement of the FR2 DU cell
|
||||
5. The UE Context Setup Request contains the UE Capabilities and the DRB
|
||||
to set up
|
||||
6. The UE Context Modification Request contains the DRB to remove (the same
|
||||
that is to be set up at step 5)
|
||||
7. The UE Context Setup Response contains the Cell Group Config to be sent
|
||||
to the UE. This Cell Group Config contains the RLC bearer to set up
|
||||
and all the parameters of the cell, plus the CFRA configuration that
|
||||
the UE has to use to connect to the FR2 DU
|
||||
8. The RRC Reconfiguration contains several elements:
|
||||
- `recoverPDCP` for the DRB to transfer from FR1 to FR2
|
||||
- removal of the measConfig for the FR2 DU (not necessary for
|
||||
completion of NR-DC; added for cleaner processing)
|
||||
- removal of RLC bearer from the Master Cell Group (FR1)
|
||||
- `mrdc-SecondaryCellGroupConfig` that contains an `RRCReconfiguration`
|
||||
with the Cell Group Config sent by the FR2 DU, configuring the
|
||||
RLC bearer on the FR2 Cell and all the parameters of the FR2 DU
|
||||
cell, plus the CFRA configuration the UE has to use to connect to
|
||||
the FR2 DU
|
||||
9. The Random Access is done according to the configuration sent by the
|
||||
FR2 DU
|
||||
10. After the transmission of the RAR, the connection to the FR2 DU is
|
||||
established and data traffic for the DRB is now using the FR2 cell
|
||||
@@ -62,6 +62,7 @@ MESSAGE_DEF(NAS_INIT_NOS1_IF, MESSAGE_PRIORITY_MED, nas_nos1_msg_t, nas_init_nos
|
||||
// xNB: realtime -> RRC messages
|
||||
MESSAGE_DEF(RRC_SUBFRAME_PROCESS, MESSAGE_PRIORITY_MED, RrcSubframeProcess, rrc_subframe_process)
|
||||
MESSAGE_DEF(NRRRC_FRAME_PROCESS, MESSAGE_PRIORITY_MED, NRRrcFrameProcess, nr_rrc_frame_process)
|
||||
MESSAGE_DEF(NR_RRC_NRDC_TIMEOUT, MESSAGE_PRIORITY_MED, nr_rrc_nrdc_timeout_t, nr_rrc_nrdc_timeout)
|
||||
|
||||
// eNB: RLC -> RRC messages
|
||||
MESSAGE_DEF(RLC_SDU_INDICATION, MESSAGE_PRIORITY_MED, RlcSduIndication, rlc_sdu_indication)
|
||||
|
||||
@@ -79,6 +79,7 @@
|
||||
|
||||
#define RRC_SUBFRAME_PROCESS(mSGpTR) (mSGpTR)->ittiMsg.rrc_subframe_process
|
||||
#define NRRRC_FRAME_PROCESS(mSGpTR) (mSGpTR)->ittiMsg.nr_rrc_frame_process
|
||||
#define NR_RRC_NRDC_TIMEOUT(mSGpTR) (mSGpTR)->ittiMsg.nr_rrc_nrdc_timeout
|
||||
|
||||
#define RLC_SDU_INDICATION(mSGpTR) (mSGpTR)->ittiMsg.rlc_sdu_indication
|
||||
#define NRDuDlReq(mSGpTR) (mSGpTR)->ittiMsg.nr_du_dl_req
|
||||
@@ -484,4 +485,10 @@ typedef struct {
|
||||
} payload;
|
||||
} nr_mac_rrc_message_t;
|
||||
|
||||
typedef struct {
|
||||
uint64_t ue_id;
|
||||
int xid;
|
||||
int state;
|
||||
} nr_rrc_nrdc_timeout_t;
|
||||
|
||||
#endif /* RRC_MESSAGES_TYPES_H_ */
|
||||
|
||||
@@ -1278,6 +1278,26 @@ static void config_pdcp(configmodule_interface_t *cfg, nr_pdcp_configuration_t *
|
||||
pdcp_config->drb.discard_timer = config_get_processedint(cfg, &pdcp_params[CONFIG_NR_PDCP_DRB_DISCARD_TIMER_IDX]);
|
||||
}
|
||||
|
||||
static void config_nrdc(configmodule_interface_t *cfg, nrdc_configuration_t *nrdc_config)
|
||||
{
|
||||
GET_PARAMS_LIST(nrdc_combination_list, nrdc_combination, NRDC_PARAMS_DESC, CONFIG_NRDC_COMBINATION_LIST, CONFIG_STRING_NRDC);
|
||||
|
||||
nrdc_config->combination_count = nrdc_combination_list.numelt;
|
||||
|
||||
LOG_I(NR_RRC, "NR-DC: %d combinations configured\n", nrdc_config->combination_count);
|
||||
|
||||
nrdc_config->combinations = calloc_or_fail(nrdc_config->combination_count, sizeof(nrdc_combination_t));
|
||||
for (int i = 0; i < nrdc_combination_list.numelt; i++) {
|
||||
const paramdef_t *combination = nrdc_combination_list.paramarray[i];
|
||||
int mcg = *gpd(combination, sizeofArray(nrdc_combination), CONFIG_NRDC_MCG)->iptr;
|
||||
int scg = *gpd(combination, sizeofArray(nrdc_combination), CONFIG_NRDC_SCG)->iptr;
|
||||
LOG_I(NR_RRC, "NR-DC: combination %d: mcg band %d scg band %d\n", i, mcg, scg);
|
||||
|
||||
nrdc_config->combinations[i].mcg_band = mcg;
|
||||
nrdc_config->combinations[i].scg_band = scg;
|
||||
}
|
||||
}
|
||||
|
||||
void nfapi_stop_l1()
|
||||
{
|
||||
if (NFAPI_MODE && (NFAPI_MODE == NFAPI_MODE_AERIAL || NFAPI_MODE == NFAPI_MODE_VNF)) {
|
||||
@@ -2452,6 +2472,8 @@ gNB_RRC_INST *RCconfig_NRRRC()
|
||||
config_rlc(config_get_if(), &rrc->rlc_config);
|
||||
config_pdcp(config_get_if(), &rrc->pdcp_config);
|
||||
|
||||
config_nrdc(config_get_if(), &rrc->nrdc_config);
|
||||
|
||||
return rrc;
|
||||
}
|
||||
|
||||
|
||||
@@ -1060,4 +1060,18 @@ typedef enum {
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
#define CONFIG_STRING_NRDC "nrdc"
|
||||
|
||||
#define CONFIG_NRDC_COMBINATION_LIST "combinations"
|
||||
|
||||
#define CONFIG_NRDC_MCG "mcg"
|
||||
#define CONFIG_NRDC_SCG "scg"
|
||||
|
||||
#define NRDC_PARAMS_DESC { \
|
||||
{ CONFIG_NRDC_MCG, "mcg", PARAMFLAG_MANDATORY, .iptr = NULL, .defintarrayval = 0, TYPE_INT, 0 }, \
|
||||
{ CONFIG_NRDC_SCG, "scg", PARAMFLAG_MANDATORY, .iptr = NULL, .defintarrayval = 0, TYPE_INT, 0 }, \
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
#endif
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "lib/f1ap_rrc_message_transfer.h"
|
||||
#include "lib/f1ap_interface_management.h"
|
||||
#include "lib/f1ap_ue_context.h"
|
||||
#include "nrdc_mac_rrc_dl_handler.h"
|
||||
|
||||
#include "executables/softmodem-common.h"
|
||||
|
||||
@@ -288,12 +289,12 @@ static NR_QoS_config_t get_qos_config(const f1ap_qos_flow_param_t *qos)
|
||||
return qos_c;
|
||||
}
|
||||
|
||||
static int handle_ue_context_drbs_setup(NR_UE_info_t *UE,
|
||||
int drbs_len,
|
||||
const f1ap_drb_to_setup_t *req_drbs,
|
||||
f1ap_drb_setup_t **resp_drbs,
|
||||
NR_CellGroupConfig_t *cellGroupConfig,
|
||||
const nr_rlc_configuration_t *rlc_config)
|
||||
int handle_ue_context_drbs_setup(NR_UE_info_t *UE,
|
||||
int drbs_len,
|
||||
const f1ap_drb_to_setup_t *req_drbs,
|
||||
f1ap_drb_setup_t **resp_drbs,
|
||||
NR_CellGroupConfig_t *cellGroupConfig,
|
||||
const nr_rlc_configuration_t *rlc_config)
|
||||
{
|
||||
DevAssert(req_drbs != NULL && resp_drbs != NULL && cellGroupConfig != NULL);
|
||||
instance_t f1inst = get_f1_gtp_instance();
|
||||
@@ -459,7 +460,7 @@ static NR_UE_NR_Capability_t *get_ue_nr_cap_from_ho_prep_info(uint8_t *buf, uint
|
||||
return cap;
|
||||
}
|
||||
|
||||
static NR_CG_ConfigInfo_t *get_cg_config_info(uint8_t *buf, uint32_t len)
|
||||
NR_CG_ConfigInfo_t *get_cg_config_info(uint8_t *buf, uint32_t len)
|
||||
{
|
||||
struct NR_CG_ConfigInfo *cg_configinfo = NULL;
|
||||
asn_dec_rval_t dec_rval = uper_decode_complete(NULL, &asn_DEF_NR_CG_ConfigInfo, (void **)&cg_configinfo, buf, len);
|
||||
@@ -471,7 +472,7 @@ static NR_CG_ConfigInfo_t *get_cg_config_info(uint8_t *buf, uint32_t len)
|
||||
return cg_configinfo;
|
||||
}
|
||||
|
||||
static NR_UE_NR_Capability_t *get_ue_nr_cap_from_cg_config_info(const NR_CG_ConfigInfo_t *cgci)
|
||||
NR_UE_NR_Capability_t *get_ue_nr_cap_from_cg_config_info(const NR_CG_ConfigInfo_t *cgci)
|
||||
{
|
||||
/* INTO DU handler */
|
||||
if (cgci->criticalExtensions.present != NR_CG_ConfigInfo__criticalExtensions_PR_c1)
|
||||
@@ -588,7 +589,7 @@ static NR_UE_info_t *create_new_UE(gNB_MAC_INST *mac, uint32_t cu_id, const NR_C
|
||||
* decode/re-encode cycles per TS 38.473 transparency requirements.
|
||||
* @param cellGroup CellGroupConfig to encode
|
||||
* @return Encoded byte array */
|
||||
static byte_array_t encode_cellgroup_config(const NR_CellGroupConfig_t *cellGroup)
|
||||
byte_array_t encode_cellgroup_config(const NR_CellGroupConfig_t *cellGroup)
|
||||
{
|
||||
byte_array_t cgc = {0};
|
||||
ssize_t encoded = uper_encode_to_new_buffer(&asn_DEF_NR_CellGroupConfig, NULL, cellGroup, (void **)&cgc.buf);
|
||||
@@ -692,11 +693,13 @@ void ue_context_setup_request(const f1ap_ue_context_setup_req_t *req)
|
||||
if (cu2du->meas_timing_config != NULL)
|
||||
mtc = get_nr_mtc(cu2du->meas_timing_config->buf, cu2du->meas_timing_config->len);
|
||||
|
||||
/* 38.473: "For DC operation, the CG-ConfigInfo IE shall be included in the CU
|
||||
* to DU RRC Information IE at the gNB acting as secondary node" As of now,
|
||||
* we only handle NSA => we check we have CG-ConfigInfo if not SA or have SA
|
||||
* and no CG-ConfigInfo */
|
||||
AssertFatal(is_SA ^ (cg_configinfo != NULL), "cannot have SA and CG-ConfigInfo: NR-DC not supported xor need CG-ConfigInfo for NSA/phy-test/do-ra\n");
|
||||
/* inclusion of CG-ConfigInfo in SA mode is interpreted
|
||||
* as NR-DC activation in the DU
|
||||
*/
|
||||
if (is_SA && cg_configinfo)
|
||||
return nrdc_ue_context_setup_request(req);
|
||||
|
||||
AssertFatal(is_SA || (cg_configinfo != NULL), "CG-ConfigInfo needed for NSA/phy-test/do-ra\n");
|
||||
|
||||
NR_SCHED_LOCK(&mac->sched_lock);
|
||||
|
||||
@@ -792,6 +795,15 @@ void ue_context_modification_request(const f1ap_ue_context_mod_req_t *req)
|
||||
.gNB_DU_ue_id = req->gNB_DU_ue_id,
|
||||
};
|
||||
|
||||
/* special handling for NR-DC UEs */
|
||||
NR_SCHED_LOCK(&mac->sched_lock);
|
||||
NR_UE_info_t *UE = find_nr_UE(&RC.nrmac[0]->UE_info, req->gNB_DU_ue_id);
|
||||
if (UE->nrdc_mode) {
|
||||
NR_SCHED_UNLOCK(&mac->sched_lock);
|
||||
return nrdc_ue_context_modification_request(req);
|
||||
}
|
||||
NR_SCHED_UNLOCK(&mac->sched_lock);
|
||||
|
||||
NR_UE_NR_Capability_t *ue_cap = NULL;
|
||||
if (req->cu_to_du_rrc_info != NULL) {
|
||||
AssertFatal(req->cu_to_du_rrc_info->cg_configinfo == NULL, "CG-ConfigInfo not handled\n");
|
||||
@@ -802,7 +814,7 @@ void ue_context_modification_request(const f1ap_ue_context_mod_req_t *req)
|
||||
}
|
||||
|
||||
NR_SCHED_LOCK(&mac->sched_lock);
|
||||
NR_UE_info_t *UE = find_nr_UE(&RC.nrmac[0]->UE_info, req->gNB_DU_ue_id);
|
||||
UE = find_nr_UE(&RC.nrmac[0]->UE_info, req->gNB_DU_ue_id);
|
||||
if (!UE) {
|
||||
LOG_E(NR_MAC, "could not find UE with RNTI %04x\n", req->gNB_DU_ue_id);
|
||||
NR_SCHED_UNLOCK(&mac->sched_lock);
|
||||
@@ -884,6 +896,29 @@ void ue_context_modification_request(const f1ap_ue_context_mod_req_t *req)
|
||||
UE->reconfigCellGroup = new_CellGroup;
|
||||
configure_UE_BWP(mac, scc, UE, false, NR_SearchSpace__searchSpaceType_PR_common, -1, -1);
|
||||
} else {
|
||||
/* hack: for NR-DC, remove the RLC bearer from the MCG group config
|
||||
* of the UE. This bearer is transfered to the SCG.
|
||||
* We check if the modification request asks to remove a bearer and adds
|
||||
* no bearer. If yes, we are doing the NR-DC scenario (hopefully).
|
||||
* This hack has to be removed in the future. (No other solution
|
||||
* was found.)
|
||||
*/
|
||||
if (req->drbs_rel_len == 1 && req->srbs_len == 0 && req->drbs_len == 0
|
||||
&& req->cu_to_du_rrc_info == NULL && req->rrc_container == NULL) {
|
||||
/* remove the RLC from UE->CellGroup->rlc_BearerToAddModList */
|
||||
long lcid = get_lcid_from_drbid(req->drbs_rel[0].id);
|
||||
LOG_I(NR_MAC, "NR-DC hack: remove RLC lcid %ld from UE cell group config\n", lcid);
|
||||
NR_CellGroupConfig_t *cellGroupConfig = UE->CellGroup;
|
||||
int idx = 0;
|
||||
while (idx < cellGroupConfig->rlc_BearerToAddModList->list.count) {
|
||||
const NR_RLC_BearerConfig_t *bc = cellGroupConfig->rlc_BearerToAddModList->list.array[idx];
|
||||
if (bc->logicalChannelIdentity == lcid)
|
||||
break;
|
||||
idx++;
|
||||
}
|
||||
DevAssert(idx < cellGroupConfig->rlc_BearerToAddModList->list.count);
|
||||
asn_sequence_del(&cellGroupConfig->rlc_BearerToAddModList->list, idx, 1);
|
||||
}
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, new_CellGroup); // we actually don't need it
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "common/platform_types.h"
|
||||
#include "f1ap_messages_types.h"
|
||||
#include "openair2/RRC/NR/MESSAGES/asn1_msg.h"
|
||||
#include "nr_mac_gNB.h"
|
||||
|
||||
void f1_reset_cu_initiated(const f1ap_reset_t *reset);
|
||||
void f1_reset_acknowledge_du_initiated(const f1ap_reset_ack_t *ack);
|
||||
@@ -23,4 +24,15 @@ void ue_context_release_command(const f1ap_ue_context_rel_cmd_t *cmd);
|
||||
|
||||
void dl_rrc_message_transfer(const f1ap_dl_rrc_message_t *dl_rrc);
|
||||
|
||||
NR_CG_ConfigInfo_t *get_cg_config_info(uint8_t *buf, uint32_t len);
|
||||
NR_UE_NR_Capability_t *get_ue_nr_cap_from_cg_config_info(const NR_CG_ConfigInfo_t *cgci);
|
||||
int handle_ue_context_drbs_setup(NR_UE_info_t *UE,
|
||||
int drbs_len,
|
||||
const f1ap_drb_to_setup_t *req_drbs,
|
||||
f1ap_drb_setup_t **resp_drbs,
|
||||
NR_CellGroupConfig_t *cellGroupConfig,
|
||||
const nr_rlc_configuration_t *rlc_config);
|
||||
|
||||
byte_array_t encode_cellgroup_config(const NR_CellGroupConfig_t *cellGroup);
|
||||
|
||||
#endif /* MAC_RRC_DL_HANDLER_H */
|
||||
|
||||
@@ -822,6 +822,8 @@ typedef struct NR_UE_info {
|
||||
// dedicated BWP is always 1 from the UE's point of view, even if the gNB has multiple BWPs.
|
||||
// The below ID is the "true" (non-consecutive) BWP ID from the gNB's point of view
|
||||
NR_BWP_Id_t local_bwp_id;
|
||||
/* if nrdc_mode is true then the DU is an SCG DU */
|
||||
bool nrdc_mode;
|
||||
} NR_UE_info_t;
|
||||
|
||||
typedef struct {
|
||||
|
||||
198
openair2/LAYER2/NR_MAC_gNB/nrdc_mac_rrc_dl_handler.c
Normal file
198
openair2/LAYER2/NR_MAC_gNB/nrdc_mac_rrc_dl_handler.c
Normal file
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
|
||||
*/
|
||||
|
||||
#include "nrdc_mac_rrc_dl_handler.h"
|
||||
|
||||
#include "common/utils/LOG/log.h"
|
||||
#include "nr_mac_gNB.h"
|
||||
#include "mac_proto.h"
|
||||
#include "mac_rrc_dl_handler.h"
|
||||
#include "openair2/F1AP/f1ap_ids.h"
|
||||
#include "openair2/F1AP/lib/f1ap_ue_context.h"
|
||||
#include "openair2/F1AP/f1ap_common.h"
|
||||
#include "openair2/LAYER2/nr_rlc/nr_rlc_oai_api.h"
|
||||
#include "openair3/ocp-gtpu/gtp_itf.h"
|
||||
|
||||
static NR_UE_info_t *nrdc_create_new_UE(gNB_MAC_INST *mac, uint32_t cu_id, const NR_CG_ConfigInfo_t *cgci)
|
||||
{
|
||||
int CC_id = 0;
|
||||
rnti_t rnti;
|
||||
bool found = nr_mac_get_new_rnti(&mac->UE_info, &rnti);
|
||||
if (!found)
|
||||
return NULL;
|
||||
|
||||
f1_ue_data_t new_ue_data = {.secondary_ue = cu_id};
|
||||
bool success = du_add_f1_ue_data(rnti, &new_ue_data);
|
||||
DevAssert(success);
|
||||
|
||||
const nr_mac_config_t *configuration = &mac->radio_config;
|
||||
NR_UE_info_t *UE = get_new_nr_ue_inst(&mac->UE_info.uid_allocator, rnti, NULL, configuration);
|
||||
AssertFatal(UE->uid < MAX_MOBILES_PER_GNB, "cannot create UE context, UE context setup failure not implemented\n");
|
||||
|
||||
NR_COMMON_channels_t *cc = &mac->common_channels[CC_id];
|
||||
const NR_ServingCellConfigCommon_t *scc = cc->ServingCellConfigCommon;
|
||||
|
||||
/* mimic NSA way to create a new UE (with adaptations) */
|
||||
NR_UE_NR_Capability_t *cap = get_ue_nr_cap_from_cg_config_info(cgci);
|
||||
int ssb_index = get_ssbidx_from_beam(mac, UE->UE_beam_index);
|
||||
NR_CellGroupConfig_t *cellGroupConfig = get_default_secondaryCellGroup(scc, cap, 1, 1, configuration, UE->uid, ssb_index);
|
||||
|
||||
cellGroupConfig->spCellConfig->reconfigurationWithSync = get_reconfiguration_with_sync(UE->rnti, UE->uid, scc, mac->frame);
|
||||
UE->capability = cap;
|
||||
UE->local_bwp_id = 1; // get_default_secondaryCellGroup sets 1st active BWP as 1
|
||||
|
||||
// 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->CellGroup = cellGroupConfig;
|
||||
|
||||
UE->nrdc_mode = true;
|
||||
|
||||
if (!add_new_UE_RA(mac, UE)) {
|
||||
delete_nr_ue_data(UE, &mac->UE_info.uid_allocator);
|
||||
LOG_E(NR_MAC, "UE list full while creating new UE\n");
|
||||
return NULL;
|
||||
}
|
||||
nr_mac_prepare_ra_ue(mac, UE);
|
||||
|
||||
return UE;
|
||||
}
|
||||
|
||||
void nrdc_ue_context_setup_request(const f1ap_ue_context_setup_req_t *req)
|
||||
{
|
||||
LOG_E(NR_MAC, "nrdc_ue_context_setup_request called!\n");
|
||||
|
||||
gNB_MAC_INST *mac = RC.nrmac[0];
|
||||
|
||||
f1ap_ue_context_setup_resp_t resp = {
|
||||
.gNB_CU_ue_id = req->gNB_CU_ue_id,
|
||||
};
|
||||
|
||||
const f1ap_cu_to_du_rrc_info_t *cu2du = &req->cu_to_du_rrc_info;
|
||||
NR_CG_ConfigInfo_t *cg_configinfo = get_cg_config_info(cu2du->cg_configinfo->buf, cu2du->cg_configinfo->len);
|
||||
|
||||
NR_SCHED_LOCK(&mac->sched_lock);
|
||||
|
||||
NR_UE_info_t *UE = nrdc_create_new_UE(mac, req->gNB_CU_ue_id, cg_configinfo);
|
||||
AssertFatal(UE, "NR-DC: cannot create a new UE, but UE Context Setup Failed not implemented yet\n");
|
||||
resp.gNB_DU_ue_id = UE->rnti;
|
||||
resp.crnti = malloc_or_fail(sizeof(*resp.crnti));
|
||||
*resp.crnti = UE->rnti;
|
||||
|
||||
NR_CellGroupConfig_t *new_CellGroup = clone_CellGroupConfig(UE->CellGroup);
|
||||
|
||||
if (req->drbs_len > 0)
|
||||
resp.drbs_len = handle_ue_context_drbs_setup(UE, req->drbs_len, req->drbs, &resp.drbs, new_CellGroup, &mac->rlc_config);
|
||||
|
||||
/* in other parts of the code, we have: UE->reconfigCellGroup = new_CellGroup;
|
||||
* but doing so crashes the DU, so let's do UE->CellGroup = new_CellGroup;
|
||||
* (to be fixed?)
|
||||
*/
|
||||
UE->CellGroup = new_CellGroup;
|
||||
int ss_type = NR_SearchSpace__searchSpaceType_PR_common;
|
||||
NR_ServingCellConfigCommon_t *scc = mac->common_channels[0].ServingCellConfigCommon;
|
||||
configure_UE_BWP(mac, scc, UE, true, ss_type, -1, -1);
|
||||
|
||||
NR_SCHED_UNLOCK(&mac->sched_lock);
|
||||
|
||||
byte_array_t cgc = { .buf = calloc_or_fail(1,1024) };
|
||||
asn_enc_rval_t enc_rval =
|
||||
uper_encode_to_buffer(&asn_DEF_NR_CellGroupConfig, NULL, new_CellGroup, cgc.buf, 1024);
|
||||
AssertFatal(enc_rval.encoded > 0, "Could not encode CellGroup, failed element %s\n", enc_rval.failed_type->name);
|
||||
cgc.len = (enc_rval.encoded + 7) >> 3;
|
||||
resp.du_to_cu_rrc_info.cell_group_config = cgc;
|
||||
|
||||
mac->mac_rrc.ue_context_setup_response(&resp);
|
||||
|
||||
/* free the memory we allocated above */
|
||||
free_ue_context_setup_resp(&resp);
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_CG_ConfigInfo, cg_configinfo);
|
||||
}
|
||||
|
||||
static instance_t get_f1_gtp_instance(void)
|
||||
{
|
||||
const f1ap_cudu_inst_t *inst = getCxt(0);
|
||||
if (!inst)
|
||||
return -1; // means no F1
|
||||
return inst->gtpInst;
|
||||
}
|
||||
|
||||
void nrdc_ue_context_modification_request(const f1ap_ue_context_mod_req_t *req)
|
||||
{
|
||||
/* this only works in F1 mode for the moment */
|
||||
instance_t f1inst = get_f1_gtp_instance();
|
||||
DevAssert(f1inst >= 0);
|
||||
|
||||
gNB_MAC_INST *mac = RC.nrmac[0];
|
||||
f1ap_ue_context_mod_resp_t resp = {
|
||||
.gNB_CU_ue_id = req->gNB_CU_ue_id,
|
||||
.gNB_DU_ue_id = req->gNB_DU_ue_id,
|
||||
};
|
||||
|
||||
NR_SCHED_LOCK(&mac->sched_lock);
|
||||
NR_UE_info_t *UE = find_nr_UE(&RC.nrmac[0]->UE_info, req->gNB_DU_ue_id);
|
||||
if (!UE) {
|
||||
LOG_E(NR_MAC, "could not find UE with RNTI %04x\n", req->gNB_DU_ue_id);
|
||||
NR_SCHED_UNLOCK(&mac->sched_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
/* only support release of 1 drb */
|
||||
if (req->srbs_len > 0 || req->drbs_len > 0 || req->rrc_container != NULL
|
||||
|| req->cu_to_du_rrc_info != NULL) {
|
||||
LOG_E(NR_MAC, "NR-DC UE Context Modification Request contains unhandled fields, ignoring\n");
|
||||
NR_SCHED_UNLOCK(&mac->sched_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
if (req->drbs_rel_len != 1) {
|
||||
LOG_E(NR_MAC, "NR-DC UE Context Modification Request contains wrong number of drbs to release (%d, expecting 1), ignoring\n", req->drbs_rel_len);
|
||||
NR_SCHED_UNLOCK(&mac->sched_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
int drb_id = req->drbs_rel[0].id;
|
||||
int lcid = get_lcid_from_drbid(drb_id);
|
||||
|
||||
/* remove the bearer everywhere it has to */
|
||||
nr_mac_remove_lcid(&UE->UE_sched_ctrl, lcid);
|
||||
nr_rlc_release_entity(UE->rnti, lcid);
|
||||
newGtpuDeleteOneTunnel(f1inst, UE->rnti, drb_id);
|
||||
|
||||
/* remove the RLC from UE->CellGroup->rlc_BearerToAddModList */
|
||||
NR_CellGroupConfig_t *cellGroupConfig = UE->CellGroup;
|
||||
int idx = 0;
|
||||
while (idx < cellGroupConfig->rlc_BearerToAddModList->list.count) {
|
||||
const NR_RLC_BearerConfig_t *bc = cellGroupConfig->rlc_BearerToAddModList->list.array[idx];
|
||||
if (bc->logicalChannelIdentity == lcid)
|
||||
break;
|
||||
idx++;
|
||||
}
|
||||
if (idx < cellGroupConfig->rlc_BearerToAddModList->list.count)
|
||||
asn_sequence_del(&cellGroupConfig->rlc_BearerToAddModList->list, idx, 1);
|
||||
|
||||
/* generate Context Modification Response */
|
||||
NR_CellGroupConfig_t cell_group = {
|
||||
.cellGroupId = 1,
|
||||
.rlc_BearerToReleaseList = &(struct NR_CellGroupConfig__rlc_BearerToReleaseList) {
|
||||
.list = {
|
||||
.array = (NR_LogicalChannelIdentity_t *[]) {
|
||||
&(NR_LogicalChannelIdentity_t) { lcid }
|
||||
},
|
||||
.count = 1
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
resp.du_to_cu_rrc_info = calloc_or_fail(1, sizeof(du_to_cu_rrc_information_t));
|
||||
resp.du_to_cu_rrc_info->cell_group_config = encode_cellgroup_config(&cell_group);
|
||||
|
||||
NR_SCHED_UNLOCK(&mac->sched_lock);
|
||||
|
||||
mac->mac_rrc.ue_context_modification_response(&resp);
|
||||
|
||||
free_ue_context_mod_resp(&resp);
|
||||
}
|
||||
13
openair2/LAYER2/NR_MAC_gNB/nrdc_mac_rrc_dl_handler.h
Normal file
13
openair2/LAYER2/NR_MAC_gNB/nrdc_mac_rrc_dl_handler.h
Normal file
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
|
||||
*/
|
||||
|
||||
#ifndef NRDC_MAC_RRC_DL_HANDLER_H
|
||||
#define NRDC_MAC_RRC_DL_HANDLER_H
|
||||
|
||||
#include "f1ap_messages_types.h"
|
||||
|
||||
void nrdc_ue_context_setup_request(const f1ap_ue_context_setup_req_t *req);
|
||||
void nrdc_ue_context_modification_request(const f1ap_ue_context_mod_req_t *req);
|
||||
|
||||
#endif /* NRDC_MAC_RRC_DL_HANDLER_H */
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "asn1_msg.h"
|
||||
#include "../nr_rrc_proto.h"
|
||||
#include "LAYER2/nr_pdcp/nr_pdcp_asn1_utils.h"
|
||||
#include "openair2/RRC/NR/rrc_gNB_measurements.h"
|
||||
|
||||
#include "openair3/SECU/key_nas_deriver.h"
|
||||
|
||||
@@ -1167,7 +1168,8 @@ static NR_MeasIdToAddMod_t *get_MeasId(NR_MeasId_t measId, NR_ReportConfigId_t r
|
||||
return measid;
|
||||
}
|
||||
|
||||
NR_MeasConfig_t *get_MeasConfig(const NR_MeasTiming_t *mt,
|
||||
NR_MeasConfig_t *get_MeasConfig(gNB_RRC_UE_t *ue,
|
||||
const NR_MeasTiming_t *mt,
|
||||
int band,
|
||||
int nr_pci,
|
||||
NR_ReportConfigToAddMod_t *rc_PER,
|
||||
@@ -1211,7 +1213,8 @@ NR_MeasConfig_t *get_MeasConfig(const NR_MeasTiming_t *mt,
|
||||
// cell-specific offsets, blacklisted cells to be ignored and whitelisted cells to consider for measurements.
|
||||
|
||||
// Serving cell
|
||||
NR_MeasObjectToAddMod_t *mo1 = get_MeasObject(ft, band, ft->carrierFreq, 1);
|
||||
int serving_cell_measobj_id = allocate_measurement_object_id(ue);
|
||||
NR_MeasObjectToAddMod_t *mo1 = get_MeasObject(ft, band, ft->carrierFreq, serving_cell_measobj_id);
|
||||
NR_MeasObjectNR_t *monr1 = mo1->measObject.choice.measObjectNR;
|
||||
monr1->cellsToAddModList = calloc_or_fail(1, sizeof(*monr1->cellsToAddModList));
|
||||
NR_CellsToAddMod_t *cell = calloc_or_fail(1, sizeof(*cell));
|
||||
@@ -1219,10 +1222,16 @@ NR_MeasConfig_t *get_MeasConfig(const NR_MeasTiming_t *mt,
|
||||
ASN_SEQUENCE_ADD(&monr1->cellsToAddModList->list, cell);
|
||||
asn1cSeqAdd(&mc->measObjectToAddModList->list, mo1);
|
||||
|
||||
int *neighbour_measurement_object_id = NULL;
|
||||
if (neigh_seq && neigh_seq->size)
|
||||
neighbour_measurement_object_id = calloc_or_fail(neigh_seq->size, sizeof(int));
|
||||
|
||||
// Neighbour cells
|
||||
if (neigh_seq) {
|
||||
int mo_id = 2;
|
||||
int i = 0;
|
||||
FOR_EACH_SEQ_ARR(nr_neighbour_cell_t *, neigh_cell, neigh_seq) {
|
||||
int mo_id = allocate_measurement_object_id(ue);
|
||||
neighbour_measurement_object_id[i] = mo_id;
|
||||
NR_MeasObjectToAddMod_t *mo_neighbour = get_MeasObject(ft, neigh_cell->band, neigh_cell->absoluteFrequencySSB, mo_id);
|
||||
NR_MeasObjectNR_t *monr = mo_neighbour->measObject.choice.measObjectNR;
|
||||
monr->cellsToAddModList = calloc_or_fail(1, sizeof(*monr->cellsToAddModList));
|
||||
@@ -1230,7 +1239,7 @@ NR_MeasConfig_t *get_MeasConfig(const NR_MeasTiming_t *mt,
|
||||
cell->physCellId = neigh_cell->physicalCellId;
|
||||
ASN_SEQUENCE_ADD(&monr->cellsToAddModList->list, cell);
|
||||
asn1cSeqAdd(&mc->measObjectToAddModList->list, mo_neighbour);
|
||||
mo_id++;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1240,19 +1249,20 @@ NR_MeasConfig_t *get_MeasConfig(const NR_MeasTiming_t *mt,
|
||||
// object.
|
||||
|
||||
// MeasId for Periodic report
|
||||
int meas_idx = 0;
|
||||
if (rc_PER) {
|
||||
for (; meas_idx < mc->measObjectToAddModList->list.count; meas_idx++) {
|
||||
const NR_MeasObjectId_t measObjectId = mc->measObjectToAddModList->list.array[meas_idx]->measObjectId;
|
||||
NR_MeasIdToAddMod_t *measid = get_MeasId(meas_idx + 1, rc_PER->reportConfigId, measObjectId);
|
||||
for (int i = 0; i < mc->measObjectToAddModList->list.count; i++) {
|
||||
const NR_MeasObjectId_t measObjectId = mc->measObjectToAddModList->list.array[i]->measObjectId;
|
||||
int meas_idx = allocate_measurement_id(ue);
|
||||
NR_MeasIdToAddMod_t *measid = get_MeasId(meas_idx, rc_PER->reportConfigId, measObjectId);
|
||||
asn1cSeqAdd(&mc->measIdToAddModList->list, measid);
|
||||
}
|
||||
}
|
||||
|
||||
// MeasId for Event A2
|
||||
if (rc_A2) {
|
||||
NR_MeasIdToAddMod_t *measid_A2 = get_MeasId(meas_idx + 1, rc_A2->reportConfigId, 1);
|
||||
meas_idx++;
|
||||
int meas_idx = allocate_measurement_id(ue);
|
||||
int meas_obj_id = serving_cell_measobj_id;
|
||||
NR_MeasIdToAddMod_t *measid_A2 = get_MeasId(meas_idx, rc_A2->reportConfigId, meas_obj_id);
|
||||
asn1cSeqAdd(&mc->measIdToAddModList->list, measid_A2);
|
||||
}
|
||||
|
||||
@@ -1266,8 +1276,9 @@ NR_MeasConfig_t *get_MeasConfig(const NR_MeasTiming_t *mt,
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
NR_MeasIdToAddMod_t *measid_A3 = get_MeasId(meas_idx + 1, reportConfigId, i + 2);
|
||||
meas_idx++;
|
||||
int meas_idx = allocate_measurement_id(ue);
|
||||
int meas_obj_id = neighbour_measurement_object_id[i];
|
||||
NR_MeasIdToAddMod_t *measid_A3 = get_MeasId(meas_idx, reportConfigId, meas_obj_id);
|
||||
asn1cSeqAdd(&mc->measIdToAddModList->list, measid_A3);
|
||||
i++;
|
||||
}
|
||||
@@ -1282,6 +1293,8 @@ NR_MeasConfig_t *get_MeasConfig(const NR_MeasTiming_t *mt,
|
||||
asn1cCallocOne(qcnr->quantityConfigCell.csi_RS_FilterConfig.filterCoefficientRSRP, NR_FilterCoefficient_fc6);
|
||||
asn1cSeqAdd(&mc->quantityConfig->quantityConfigNR_List->list, qcnr);
|
||||
|
||||
free(neighbour_measurement_object_id);
|
||||
|
||||
return mc;
|
||||
}
|
||||
|
||||
|
||||
@@ -143,7 +143,10 @@ int do_RRCReestablishment(int8_t nh_ncc, uint8_t *const buffer, size_t buffer_si
|
||||
|
||||
int do_RRCReestablishmentComplete(uint8_t *buffer, size_t buffer_size, int64_t rrc_TransactionIdentifier);
|
||||
|
||||
NR_MeasConfig_t *get_MeasConfig(const NR_MeasTiming_t *mt,
|
||||
#include "openair2/RRC/NR/nr_rrc_defs.h"
|
||||
|
||||
NR_MeasConfig_t *get_MeasConfig(gNB_RRC_UE_t *ue,
|
||||
const NR_MeasTiming_t *mt,
|
||||
int band,
|
||||
int nr_pci,
|
||||
NR_ReportConfigToAddMod_t *rc_PER,
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include "openair2/LAYER2/nr_pdcp/nr_pdcp_configuration.h"
|
||||
#include "openair2/LAYER2/nr_rlc/nr_rlc_configuration.h"
|
||||
#include "openair2/SDAP/nr_sdap/nr_sdap_configuration.h"
|
||||
#include "openair2/RRC/NR/nrdc_configuration.h"
|
||||
|
||||
typedef enum {
|
||||
NR_RRC_OK=0,
|
||||
@@ -45,10 +46,6 @@ typedef enum {
|
||||
NR_RRC_HO_STARTED
|
||||
} NR_RRC_status_t;
|
||||
|
||||
#define MAX_MEAS_OBJ 7
|
||||
#define MAX_MEAS_CONFIG 7
|
||||
#define MAX_MEAS_ID 7
|
||||
|
||||
#define UNDEF_SECURITY_MODE 0xff
|
||||
#define NO_SECURITY_MODE 0x20
|
||||
|
||||
@@ -123,6 +120,7 @@ typedef enum {
|
||||
RRC_PDUSESSION_MODIFY,
|
||||
RRC_PDUSESSION_RELEASE,
|
||||
RRC_UECAPABILITY_ENQUIRY,
|
||||
RRC_F1_NRDC_IN_PROGRESS,
|
||||
} rrc_action_t;
|
||||
|
||||
typedef struct nr_rrc_config {
|
||||
@@ -265,6 +263,14 @@ typedef struct gNB_RRC_UE_s {
|
||||
delayed_action_state_t delayed_action;
|
||||
|
||||
nr_redcap_ue_cap_t *redcap_cap;
|
||||
|
||||
/* measurement IDs */
|
||||
uint64_t measurement_object_ids; /* bitfield, 64 possible values */
|
||||
uint64_t measurement_ids; /* bitfield, 64 possible values */
|
||||
uint64_t report_config_ids; /* bitfield, 64 possible values */
|
||||
|
||||
/* opaque pointer to store NR-DC state (NULL when no NR-DC) */
|
||||
void *nrdc;
|
||||
} gNB_RRC_UE_t;
|
||||
|
||||
typedef struct rrc_gNB_ue_context_s {
|
||||
@@ -645,6 +651,8 @@ typedef struct gNB_RRC_INST_s {
|
||||
// PDCP configuration parameters loaded during startup
|
||||
nr_pdcp_configuration_t pdcp_config;
|
||||
nr_rlc_configuration_t rlc_config;
|
||||
|
||||
nrdc_configuration_t nrdc_config;
|
||||
} gNB_RRC_INST;
|
||||
|
||||
/** Forward declaration for UE log macros */
|
||||
|
||||
@@ -45,7 +45,7 @@ void *rrc_gnb_task(void *args_p);
|
||||
|
||||
int nr_rrc_reconfiguration_req(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue_p);
|
||||
|
||||
NR_MeasConfig_t *nr_rrc_get_measconfig(const gNB_RRC_INST *rrc, uint64_t nr_cellid);
|
||||
NR_MeasConfig_t *nr_rrc_get_measconfig(const gNB_RRC_INST *rrc, uint64_t nr_cellid, gNB_RRC_UE_t *ue);
|
||||
|
||||
bool ue_associated_to_cuup(const gNB_RRC_UE_t *ue);
|
||||
sctp_assoc_t get_existing_cuup_for_ue(const gNB_RRC_UE_t *ue);
|
||||
@@ -88,6 +88,7 @@ byte_array_t rrc_gNB_encode_RRCReconfiguration(gNB_RRC_UE_t *UE, nr_rrc_reconfig
|
||||
|
||||
nr_rrc_reconfig_param_t get_RRCReconfiguration_params(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, uint8_t srb_reest_bitmap, bool drb_reestablish);
|
||||
|
||||
f1ap_qos_flow_param_t nr_rrc_get_f1_qos_flow_param(const pdusession_level_qos_parameter_t *qos_param);
|
||||
void openair_rrc_gNB_configuration(gNB_RRC_INST *rrc, nr_rrc_config_t *configuration);
|
||||
NR_SRB_ToAddModList_t *createSRBlist(gNB_RRC_UE_t *ue, uint8_t reestablish);
|
||||
NR_DRB_ToAddModList_t *createDRBlist(gNB_RRC_UE_t *ue, bool reestablish, bool do_integrity, bool do_ciphering);
|
||||
@@ -105,4 +106,15 @@ void rrc_f1_ue_context_setup_for_target_du(const gNB_RRC_INST *rrc,
|
||||
const nr_rrc_cell_container_t *cell,
|
||||
const byte_array_t *ho_prep_info);
|
||||
const neighbour_cell_configuration_t *get_neighbour_cell_config(const gNB_RRC_INST *rrc, int cell_id);
|
||||
|
||||
void nr_rrc_transfer_protected_rrc_message(const gNB_RRC_INST *rrc,
|
||||
const gNB_RRC_UE_t *ue_p,
|
||||
uint8_t srb_id,
|
||||
const uint32_t message_id,
|
||||
const uint8_t *buffer,
|
||||
int size);
|
||||
|
||||
void store_du_f1u_tunnel(const f1ap_drb_setup_t *drbs, int n, gNB_RRC_UE_t *ue);
|
||||
void e1_send_bearer_updates(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, int n, f1ap_drb_setup_t *drbs);
|
||||
|
||||
#endif
|
||||
|
||||
18
openair2/RRC/NR/nrdc_configuration.h
Normal file
18
openair2/RRC/NR/nrdc_configuration.h
Normal file
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
|
||||
*/
|
||||
|
||||
#ifndef _NRDC_CONFIGURATION_H_
|
||||
#define _NRDC_CONFIGURATION_H_
|
||||
|
||||
typedef struct {
|
||||
int mcg_band;
|
||||
int scg_band;
|
||||
} nrdc_combination_t;
|
||||
|
||||
typedef struct {
|
||||
int combination_count;
|
||||
nrdc_combination_t *combinations;
|
||||
} nrdc_configuration_t;
|
||||
|
||||
#endif /* _NRDC_CONFIGURATION_H_ */
|
||||
@@ -127,6 +127,18 @@ nr_rrc_cell_container_t *get_cell_by_cell_id(struct rrc_cell_tree *cells, const
|
||||
return RB_FIND(rrc_cell_tree, cells, &search_key);
|
||||
}
|
||||
|
||||
/* return the first cell matching the wanted band */
|
||||
nr_rrc_cell_container_t *get_cell_by_band(struct rrc_cell_tree *cells, int band)
|
||||
{
|
||||
nr_rrc_cell_container_t *cell;
|
||||
RB_FOREACH(cell, rrc_cell_tree, cells) {
|
||||
int cell_band = cell->info.mode == NR_MODE_TDD ? cell->info.tdd.dlul.band : cell->info.fdd.dl.band;
|
||||
if (cell_band == band)
|
||||
return cell;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** @brief Add cell to global cell tree and increment rrc->num_cells on success
|
||||
* @param[in] rrc Pointer to RRC instance
|
||||
* @param[in] cell Pointer to cell container to add
|
||||
|
||||
@@ -38,6 +38,8 @@ nr_rrc_cell_container_t *get_cell_by_cell_id(struct rrc_cell_tree *cells, const
|
||||
nr_rrc_cell_container_t *rrc_add_cell(gNB_RRC_INST *rrc, nr_rrc_cell_container_t *cell);
|
||||
void rrc_rm_cell(gNB_RRC_INST *rrc, nr_rrc_cell_container_t *cell);
|
||||
|
||||
nr_rrc_cell_container_t *get_cell_by_band(struct rrc_cell_tree *cells, int band);
|
||||
|
||||
// Cell management (DU-specific cell tree)
|
||||
nr_rrc_cell_container_t *rrc_get_cell_for_du(seq_arr_t *cells, uint64_t cell_id);
|
||||
nr_rrc_cell_container_t *rrc_get_cell_by_pci_for_du(const seq_arr_t *cells, uint16_t pci);
|
||||
|
||||
@@ -81,6 +81,8 @@
|
||||
#include "alg/find.h"
|
||||
#include "NR_HandoverCommand.h"
|
||||
#include "openair2/SDAP/nr_sdap/nr_sdap_configuration.h"
|
||||
#include "rrc_gNB_measurements.h"
|
||||
#include "rrc_gNB_nrdc.h"
|
||||
|
||||
#ifdef E2_AGENT
|
||||
#include "openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_rc_extern.h"
|
||||
@@ -294,12 +296,12 @@ static void rrc_deliver_dl_rrc_message(void *deliver_pdu_data, ue_id_t ue_id, in
|
||||
data->rrc->mac_rrc.dl_rrc_message_transfer(data->assoc_id, data->dl_rrc);
|
||||
}
|
||||
|
||||
static void nr_rrc_transfer_protected_rrc_message(const gNB_RRC_INST *rrc,
|
||||
const gNB_RRC_UE_t *ue_p,
|
||||
uint8_t srb_id,
|
||||
const uint32_t message_id,
|
||||
const uint8_t *buffer,
|
||||
int size)
|
||||
void nr_rrc_transfer_protected_rrc_message(const gNB_RRC_INST *rrc,
|
||||
const gNB_RRC_UE_t *ue_p,
|
||||
uint8_t srb_id,
|
||||
const uint32_t message_id,
|
||||
const uint8_t *buffer,
|
||||
int size)
|
||||
{
|
||||
DevAssert(size > 0);
|
||||
f1_ue_data_t ue_data = cu_get_f1_ue_data(ue_p->rrc_ue_id);
|
||||
@@ -656,10 +658,10 @@ static nr_a3_event_t *get_a3_configuration(gNB_RRC_INST *rrc, int pci)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static NR_ReportConfigToAddMod_t *prepare_periodic_event_report(const nr_per_event_t *per_event)
|
||||
static NR_ReportConfigToAddMod_t *prepare_periodic_event_report(gNB_RRC_UE_t *ue, const nr_per_event_t *per_event)
|
||||
{
|
||||
NR_ReportConfigToAddMod_t *rc = calloc(1, sizeof(*rc));
|
||||
rc->reportConfigId = 1;
|
||||
rc->reportConfigId = allocate_report_config_id(ue);
|
||||
rc->reportConfig.present = NR_ReportConfigToAddMod__reportConfig_PR_reportConfigNR;
|
||||
|
||||
NR_PeriodicalReportConfig_t *prc = calloc(1, sizeof(*prc));
|
||||
@@ -685,10 +687,10 @@ static NR_ReportConfigToAddMod_t *prepare_periodic_event_report(const nr_per_eve
|
||||
return rc;
|
||||
}
|
||||
|
||||
static NR_ReportConfigToAddMod_t *prepare_a2_event_report(const nr_a2_event_t *a2_event)
|
||||
static NR_ReportConfigToAddMod_t *prepare_a2_event_report(gNB_RRC_UE_t *ue, const nr_a2_event_t *a2_event)
|
||||
{
|
||||
NR_ReportConfigToAddMod_t *rc_A2 = calloc(1, sizeof(*rc_A2));
|
||||
rc_A2->reportConfigId = 2;
|
||||
rc_A2->reportConfigId = allocate_report_config_id(ue);
|
||||
rc_A2->reportConfig.present = NR_ReportConfigToAddMod__reportConfig_PR_reportConfigNR;
|
||||
NR_EventTriggerConfig_t *etrc_A2 = calloc(1, sizeof(*etrc_A2));
|
||||
etrc_A2->eventId.present = NR_EventTriggerConfig__eventId_PR_eventA2;
|
||||
@@ -756,7 +758,7 @@ void free_RRCReconfiguration_params(nr_rrc_reconfig_param_t params)
|
||||
FREE_AND_ZERO_BYTE_ARRAY(params.dedicated_NAS_msg_list[i]);
|
||||
}
|
||||
|
||||
NR_MeasConfig_t *nr_rrc_get_measconfig(const gNB_RRC_INST *rrc, uint64_t nr_cellid)
|
||||
NR_MeasConfig_t *nr_rrc_get_measconfig(const gNB_RRC_INST *rrc, uint64_t nr_cellid, gNB_RRC_UE_t *ue)
|
||||
{
|
||||
nr_rrc_cell_container_t *cell = get_cell_by_cell_id(&((gNB_RRC_INST *)rrc)->cells, nr_cellid);
|
||||
DevAssert(cell != NULL);
|
||||
@@ -790,6 +792,7 @@ NR_MeasConfig_t *nr_rrc_get_measconfig(const gNB_RRC_INST *rrc, uint64_t nr_cell
|
||||
*/
|
||||
LOG_D(NR_RRC, "Preparing A3 Event Measurement Configuration!\n");
|
||||
bool default_a3_added = false; // To ensure that the default configuration is only added once
|
||||
int default_a3_report_config_id = -1;
|
||||
int i = 0;
|
||||
FOR_EACH_SEQ_ARR(nr_neighbour_cell_t *, neighbourCell, neighbour_cells) {
|
||||
seq_arr_push_back(&neigh_seq, neighbourCell, sizeof(nr_neighbour_cell_t));
|
||||
@@ -798,7 +801,7 @@ NR_MeasConfig_t *nr_rrc_get_measconfig(const gNB_RRC_INST *rrc, uint64_t nr_cell
|
||||
/* no A3 event configured for this neighbour, let's try the default one, if it exists */
|
||||
if (default_a3_added) {
|
||||
/* default A3 exists and is already added, use it for this neighbour */
|
||||
neigh_a3_id[i] = 3;
|
||||
neigh_a3_id[i] = default_a3_report_config_id;
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
@@ -810,12 +813,13 @@ NR_MeasConfig_t *nr_rrc_get_measconfig(const gNB_RRC_INST *rrc, uint64_t nr_cell
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
/* allocated default A3 report config ID */
|
||||
default_a3_report_config_id = allocate_report_config_id(ue);
|
||||
neigh_a3_id[i] = default_a3_report_config_id;
|
||||
default_a3_added = true;
|
||||
/* default A3 report config ID is 3 */
|
||||
neigh_a3_id[i] = 3;
|
||||
} else {
|
||||
/* specific A3 report config ID are 4, 5, ... */
|
||||
neigh_a3_id[i] = i + 4;
|
||||
/* allocate specific A3 report config ID */
|
||||
neigh_a3_id[i] = allocate_report_config_id(ue);
|
||||
}
|
||||
NR_ReportConfigId_t reportConfigId = neigh_a3_id[i];
|
||||
seq_arr_push_back(&rc_A3_seq, prepare_a3_event_report(a3Event, reportConfigId), sizeof(NR_ReportConfigToAddMod_t));
|
||||
@@ -823,11 +827,11 @@ NR_MeasConfig_t *nr_rrc_get_measconfig(const gNB_RRC_INST *rrc, uint64_t nr_cell
|
||||
}
|
||||
}
|
||||
if (meas_cfg->per_event)
|
||||
rc_PER = prepare_periodic_event_report(meas_cfg->per_event);
|
||||
rc_PER = prepare_periodic_event_report(ue, meas_cfg->per_event);
|
||||
if (meas_cfg->a2_event)
|
||||
rc_A2 = prepare_a2_event_report(meas_cfg->a2_event);
|
||||
rc_A2 = prepare_a2_event_report(ue, meas_cfg->a2_event);
|
||||
|
||||
NR_MeasConfig_t *result = get_MeasConfig(mt, band, cell->info.pci, rc_PER, rc_A2, &rc_A3_seq, &neigh_seq, neigh_a3_id);
|
||||
NR_MeasConfig_t *result = get_MeasConfig(ue, mt, band, cell->info.pci, rc_PER, rc_A2, &rc_A3_seq, &neigh_seq, neigh_a3_id);
|
||||
|
||||
// Clean up sequence arrays
|
||||
seq_arr_free(&rc_A3_seq, NULL);
|
||||
@@ -1361,7 +1365,7 @@ static void rrc_handle_RRCSetupRequest(gNB_RRC_INST *rrc,
|
||||
return;
|
||||
}
|
||||
UE->ongoing_reconfiguration = false;
|
||||
UE->measConfig = nr_rrc_get_measconfig(rrc, msg->nr_cellid);
|
||||
UE->measConfig = nr_rrc_get_measconfig(rrc, msg->nr_cellid, UE);
|
||||
activate_srb(UE, 1);
|
||||
rrc_gNB_generate_RRCSetup(0, ue_context_p, msg->du2cu_rrc_container, msg->du2cu_rrc_container_length);
|
||||
}
|
||||
@@ -1788,6 +1792,12 @@ static void rrc_gNB_process_MeasurementReport(gNB_RRC_INST *rrc, gNB_RRC_UE_t *U
|
||||
NR_MeasurementReport_IEs_t *measurementReport_IEs = measurementReport->criticalExtensions.choice.measurementReport;
|
||||
const NR_MeasId_t measId = measurementReport_IEs->measResults.measId;
|
||||
|
||||
/* check NR-DC measurement */
|
||||
if (measId == get_scg_measurement_id(UE)) {
|
||||
rrc_gnb_nrdc_measurement_received(rrc, UE, measurementReport);
|
||||
return;
|
||||
}
|
||||
|
||||
NR_MeasIdToAddMod_t *meas_id_s = NULL;
|
||||
for (int meas_idx = 0; meas_idx < meas_config->measIdToAddModList->list.count; meas_idx++) {
|
||||
if (measId == meas_config->measIdToAddModList->list.array[meas_idx]->measId) {
|
||||
@@ -1871,6 +1881,12 @@ static void handle_ueCapabilityInformation(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE,
|
||||
int xid = ue_cap_info->rrc_TransactionIdentifier;
|
||||
rrc_action_t a = UE->xids[xid];
|
||||
UE->xids[xid] = RRC_ACTION_NONE;
|
||||
|
||||
if (a == RRC_F1_NRDC_IN_PROGRESS) {
|
||||
rrc_gnb_nrdc_ue_capabilities_received(rrc, UE, ue_cap_info);
|
||||
return;
|
||||
}
|
||||
|
||||
if (a != RRC_UECAPABILITY_ENQUIRY) {
|
||||
LOG_E(NR_RRC, "UE %d: received unsolicited UE Capability Information, aborting procedure\n", UE->rrc_ue_id);
|
||||
return;
|
||||
@@ -2069,6 +2085,8 @@ static void handle_rrcReconfigurationComplete(gNB_RRC_INST *rrc, gNB_RRC_UE_t *U
|
||||
UE->ue_reconfiguration_counter++;
|
||||
UE->ongoing_reconfiguration = false;
|
||||
|
||||
bool start_nrdc = false;
|
||||
|
||||
switch (UE->xids[xid]) {
|
||||
case RRC_PDUSESSION_RELEASE: {
|
||||
rrc_gNB_send_NGAP_PDUSESSION_RELEASE_RESPONSE(rrc, UE, xid);
|
||||
@@ -2088,6 +2106,8 @@ static void handle_rrcReconfigurationComplete(gNB_RRC_INST *rrc, gNB_RRC_UE_t *U
|
||||
"UE %d: RRC Reconfiguration Complete for PDU session establishment, but no PDU sessions were setup\n",
|
||||
UE->rrc_ue_id);
|
||||
reset_delayed_action(&UE->delayed_action);
|
||||
/* once a PDU session is established, we can start NR-DC */
|
||||
start_nrdc = true;
|
||||
break;
|
||||
case RRC_PDUSESSION_MODIFY:
|
||||
rrc_gNB_send_NGAP_PDUSESSION_MODIFY_RESP(rrc, UE, xid);
|
||||
@@ -2100,6 +2120,10 @@ static void handle_rrcReconfigurationComplete(gNB_RRC_INST *rrc, gNB_RRC_UE_t *U
|
||||
case RRC_ACTION_NONE:
|
||||
LOG_E(RRC, "UE %d: Received RRC Reconfiguration Complete with xid %d while no transaction is ongoing\n", UE->rrc_ue_id, xid);
|
||||
break;
|
||||
case RRC_F1_NRDC_IN_PROGRESS:
|
||||
rrc_gnb_nrdc_rrc_reconfiguration_complete_received(rrc, UE, xid);
|
||||
reset_delayed_action(&UE->delayed_action);
|
||||
break;
|
||||
default:
|
||||
LOG_E(RRC, "UE %d: Received unexpected transaction type %d for xid %d\n", UE->rrc_ue_id, UE->xids[xid], xid);
|
||||
break;
|
||||
@@ -2130,6 +2154,9 @@ static void handle_rrcReconfigurationComplete(gNB_RRC_INST *rrc, gNB_RRC_UE_t *U
|
||||
};
|
||||
rrc->mac_rrc.ue_context_modification_request(ue_data.du_assoc_id, &req);
|
||||
/* nothing to be freed */
|
||||
|
||||
if (start_nrdc)
|
||||
rrc_gnb_nrdc_start(rrc, UE);
|
||||
}
|
||||
|
||||
static void rrc_gNB_generate_UECapabilityEnquiry(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue)
|
||||
@@ -2262,6 +2289,24 @@ static int rrc_gNB_decode_dcch(gNB_RRC_INST *rrc, const f1ap_ul_rrc_message_t *m
|
||||
handle_rrcReestablishmentComplete(rrc, UE, rc);
|
||||
break;
|
||||
|
||||
case NR_UL_DCCH_MessageType__c1_PR_scgFailureInformation: {
|
||||
long type = ul_dcch_msg->message.choice.c1->choice.scgFailureInformation->criticalExtensions.choice.scgFailureInformation->failureReportSCG->failureType;
|
||||
char *cause[] = {
|
||||
"NR_FailureReportSCG__failureType_t310_Expiry",
|
||||
"NR_FailureReportSCG__failureType_randomAccessProblem",
|
||||
"NR_FailureReportSCG__failureType_rlc_MaxNumRetx",
|
||||
"NR_FailureReportSCG__failureType_synchReconfigFailureSCG",
|
||||
"NR_FailureReportSCG__failureType_scg_ReconfigFailure",
|
||||
"NR_FailureReportSCG__failureType_srb3_IntegrityFailure",
|
||||
"NR_FailureReportSCG__failureType_other_r16",
|
||||
"NR_FailureReportSCG__failureType_spare1"
|
||||
};
|
||||
LOG_E(NR_RRC, "scg failure cause %s\n", type >= 0 && type <= 7 ? cause[type] : "unknown");
|
||||
LOG_UE_UL_EVENT(UE, "received scg failure cause %s\n", type >= 0 && type <= 7 ? cause[type] : "unknown");
|
||||
nrdc_handle_scg_failure_information(rrc, UE, ul_dcch_msg->message.choice.c1->choice.scgFailureInformation);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -2374,7 +2419,7 @@ static gtpu_tunnel_t f1u_gtp_update(uint32_t teid, const in_addr_t addr)
|
||||
/**
|
||||
* @brief Update DRB TEID information in RRC storage from received DRB list
|
||||
*/
|
||||
static void store_du_f1u_tunnel(const f1ap_drb_setup_t *drbs, int n, gNB_RRC_UE_t *ue)
|
||||
void store_du_f1u_tunnel(const f1ap_drb_setup_t *drbs, int n, gNB_RRC_UE_t *ue)
|
||||
{
|
||||
for (int i = 0; i < n; i++) {
|
||||
const f1ap_drb_setup_t *drb_f1 = &drbs[i];
|
||||
@@ -2424,7 +2469,7 @@ static void e1_send_bearer_modification_request(gNB_RRC_INST *rrc, gNB_RRC_UE_t
|
||||
}
|
||||
|
||||
/** @brief Send E1 bearer updates for DRBs to setup from F1 UE Context Modification Response */
|
||||
static void e1_send_bearer_updates(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, int n, f1ap_drb_setup_t *drbs)
|
||||
void e1_send_bearer_updates(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, int n, f1ap_drb_setup_t *drbs)
|
||||
{
|
||||
if (!is_cuup_associated(rrc) || n <= 0)
|
||||
return;
|
||||
@@ -2571,6 +2616,9 @@ static void rrc_CU_process_ue_context_setup_response(MessageDef *msg_p, instance
|
||||
gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
|
||||
UE->f1_ue_context_active = true;
|
||||
|
||||
if (UE->nrdc && rrc_gnb_nrdc_wait_for_f1_context_setup_response(UE))
|
||||
return nrdc_rrc_CU_process_ue_context_setup_response(UE, rrc, resp);
|
||||
|
||||
// Store the encoded CellGroupConfig for transparent forwarding
|
||||
byte_array_t *cgc = &resp->du_to_cu_rrc_info.cell_group_config;
|
||||
store_cgc(UE, cgc);
|
||||
@@ -2640,6 +2688,11 @@ static void rrc_CU_process_ue_context_release_request(MessageDef *msg_p, sctp_as
|
||||
}
|
||||
|
||||
gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
|
||||
|
||||
if (UE->nrdc != NULL)
|
||||
/* UE has NR-DC connection, send release to the SCG DU */
|
||||
nrdc_scg_ue_release(rrc, UE);
|
||||
|
||||
if (UE->ho_context != NULL) {
|
||||
nr_ho_source_cu_t *source_ctx = UE->ho_context->source;
|
||||
bool from_source_du = source_ctx && source_ctx->cell->assoc_id == assoc_id;
|
||||
@@ -2692,11 +2745,14 @@ static void rrc_delete_ue_data(gNB_RRC_UE_t *UE)
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_MeasResults, UE->measResults);
|
||||
FREE_AND_ZERO_BYTE_ARRAY(UE->ue_cap_buffer);
|
||||
free_MeasConfig(UE->measConfig);
|
||||
reset_all_measurement_ids(UE);
|
||||
free(UE->redcap_cap);
|
||||
UE->redcap_cap = NULL;
|
||||
seq_arr_free(&UE->pduSessions, free_pdusession);
|
||||
seq_arr_free(&UE->drbs, free_drb);
|
||||
seq_arr_free(&UE->serving_cells, NULL);
|
||||
if (UE->nrdc)
|
||||
rrc_gnb_free_nrdc(UE);
|
||||
}
|
||||
|
||||
void rrc_remove_ue(gNB_RRC_INST *rrc, rrc_gNB_ue_context_t *ue_context_p)
|
||||
@@ -2723,6 +2779,14 @@ static void rrc_CU_process_ue_context_release_complete(MessageDef *msg_p)
|
||||
}
|
||||
|
||||
gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
|
||||
|
||||
/* check if this message is for NR-DC */
|
||||
if (UE->nrdc) {
|
||||
/* if the message is handled by the NR-DC module, do nothing more */
|
||||
if (nrdc_handle_f1_context_release_complete(rrc, UE, complete))
|
||||
return;
|
||||
}
|
||||
|
||||
if (UE->an_release) {
|
||||
/* only trigger release if it has been requested by core
|
||||
* otherwise, it might be CU that requested release on a DU during normal
|
||||
@@ -2743,6 +2807,12 @@ static void rrc_CU_process_ue_context_modification_response(MessageDef *msg_p, i
|
||||
}
|
||||
gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
|
||||
|
||||
if (UE->nrdc && rrc_gnb_nrdc_wait_for_f1_context_modification_response(UE))
|
||||
return nrdc_rrc_CU_process_ue_context_modification_response(UE, rrc, resp);
|
||||
|
||||
if (UE->nrdc && rrc_gnb_nrdc_wait_for_scg_failure_context_modification_response(UE))
|
||||
return nrdc_scg_failure_context_modification_response(UE, rrc, resp);
|
||||
|
||||
bool is_inter_cu_ho = UE->ho_context && UE->ho_context->source && !UE->ho_context->target;
|
||||
if (resp->drbs_len > 0) { // DRB to setup
|
||||
store_du_f1u_tunnel(resp->drbs, resp->drbs_len, UE);
|
||||
@@ -2873,7 +2943,7 @@ unsigned int mask_flip(unsigned int x) {
|
||||
/** @brief Get F1AP QoS flow parameters from PDU session QoS parameters
|
||||
* @param qos_param PDU session level QoS parameters from NGAP
|
||||
* @return F1AP QoS flow parameters */
|
||||
static f1ap_qos_flow_param_t nr_rrc_get_f1_qos_flow_param(const pdusession_level_qos_parameter_t *qos_param)
|
||||
f1ap_qos_flow_param_t nr_rrc_get_f1_qos_flow_param(const pdusession_level_qos_parameter_t *qos_param)
|
||||
{
|
||||
f1ap_qos_flow_param_t qos_char = {0};
|
||||
|
||||
@@ -3092,6 +3162,27 @@ static void rrc_send_f1_ue_context_modification_request(const gNB_RRC_INST *rrc,
|
||||
f1_ue_data_t ue_data = cu_get_f1_ue_data(ue_p->rrc_ue_id);
|
||||
RETURN_IF_INVALID_ASSOC_ID(ue_data.du_assoc_id);
|
||||
|
||||
/* check if one of the bearers to remove is the NR-DC one, remove it if yes */
|
||||
if (n_rel_drbs) {
|
||||
int nrdc_bearer_index;
|
||||
for (nrdc_bearer_index = 0; nrdc_bearer_index < n_rel_drbs; nrdc_bearer_index++) {
|
||||
if (is_nrdc_bearer(ue_p, rel_drbs[nrdc_bearer_index].id)) {
|
||||
nrdc_release_bearer(rrc, ue_p, rel_drbs[nrdc_bearer_index].id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (nrdc_bearer_index != n_rel_drbs) {
|
||||
/* nrdc bearer was removed, do not remove it again */
|
||||
n_rel_drbs--;
|
||||
/* do nothing else if no add/rel to do */
|
||||
if (!n_rel_drbs && !n_drbs)
|
||||
return;
|
||||
memcpy(&rel_drbs[nrdc_bearer_index], &rel_drbs[nrdc_bearer_index + 1],
|
||||
sizeof(int) * n_rel_drbs - nrdc_bearer_index);
|
||||
}
|
||||
}
|
||||
|
||||
f1ap_ue_context_mod_req_t req = {
|
||||
.gNB_CU_ue_id = ue_p->rrc_ue_id,
|
||||
.gNB_DU_ue_id = ue_data.secondary_ue,
|
||||
@@ -3698,6 +3789,10 @@ void *rrc_gnb_task(void *args_p)
|
||||
rrc_gNB_free_Handover_Command(&NGAP_HANDOVER_COMMAND(msg_p)); // Free transfered NG message
|
||||
break;
|
||||
|
||||
case NR_RRC_NRDC_TIMEOUT:
|
||||
rrc_gnb_nrdc_timeout(RC.nrrrc[instance], &NR_RRC_NRDC_TIMEOUT(msg_p));
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG_E(NR_RRC, "[gNB %ld] Received unexpected message %s\n", instance, msg_name_p);
|
||||
break;
|
||||
@@ -3753,6 +3848,10 @@ void rrc_gNB_generate_RRCRelease(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE)
|
||||
deliver_ue_ctxt_release_data_t data = {.rrc = rrc, .release_cmd = &ue_context_release_cmd, .assoc_id = ue_data.du_assoc_id};
|
||||
nr_pdcp_data_req_srb(UE->rrc_ue_id, DL_SCH_LCID_DCCH, rrc_gNB_mui++, size, buffer, rrc_deliver_ue_ctxt_release_cmd, &data);
|
||||
|
||||
/* if the UE is in NR-DC, release it from SCG DU */
|
||||
if (UE->nrdc)
|
||||
nrdc_scg_ue_release(rrc, UE);
|
||||
|
||||
#ifdef E2_AGENT
|
||||
E2_AGENT_SIGNAL_DL_DCCH_RRC_MSG(buffer, size, NR_DL_DCCH_MessageType__c1_PR_rrcRelease);
|
||||
#endif
|
||||
@@ -3930,7 +4029,8 @@ void rrc_f1_ue_context_setup_for_target_du(const gNB_RRC_INST *rrc,
|
||||
|
||||
/* Update measurement config for target DU */
|
||||
free_MeasConfig(ue->measConfig);
|
||||
ue->measConfig = nr_rrc_get_measconfig(rrc, cell->info.cell_id);
|
||||
reset_all_measurement_ids(ue);
|
||||
ue->measConfig = nr_rrc_get_measconfig(rrc, cell->info.cell_id, ue);
|
||||
byte_array_t *meas_config = calloc_or_fail(1, sizeof(*meas_config));
|
||||
meas_config->buf = calloc_or_fail(1, NR_RRC_BUF_SIZE);
|
||||
meas_config->len = do_NR_MeasConfig(ue->measConfig, meas_config->buf, NR_RRC_BUF_SIZE);
|
||||
|
||||
@@ -53,7 +53,7 @@ static int get_ssb_scs(const f1ap_served_cell_info_t *cell_info)
|
||||
return cell_info->mode == F1AP_MODE_TDD ? cell_info->tdd.tbw.scs : cell_info->fdd.dl_tbw.scs;
|
||||
}
|
||||
|
||||
static NR_SSB_MTC_t *get_ssb_mtc(const NR_MeasurementTimingConfiguration_t *mtc)
|
||||
NR_SSB_MTC_t *get_ssb_mtc(const NR_MeasurementTimingConfiguration_t *mtc)
|
||||
{
|
||||
// TODO verify which element of the list to pick
|
||||
NR_MeasTimingList_t *mtlist = mtc->criticalExtensions.choice.c1->choice.measTimingConf->measTiming;
|
||||
|
||||
81
openair2/RRC/NR/rrc_gNB_measurements.c
Normal file
81
openair2/RRC/NR/rrc_gNB_measurements.c
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
|
||||
*/
|
||||
|
||||
#include "rrc_gNB_measurements.h"
|
||||
#include "nr_rrc_defs.h"
|
||||
|
||||
int allocate_measurement_object_id(void *_ue)
|
||||
{
|
||||
/* todo(?) use common/utils/collection/linear_alloc.h */
|
||||
gNB_RRC_UE_t *ue = _ue;
|
||||
uint64_t mask = 1;
|
||||
int ret;
|
||||
for (ret = 0; ret < 64; ret++, mask <<= 1)
|
||||
if (!(ue->measurement_object_ids & mask))
|
||||
break;
|
||||
DevAssert(ret < 64);
|
||||
ue->measurement_object_ids |= mask;
|
||||
return ret + 1;
|
||||
}
|
||||
|
||||
void free_measurement_object_id(void *_ue, int mo_id)
|
||||
{
|
||||
gNB_RRC_UE_t *ue = _ue;
|
||||
uint64_t mask = (uint64_t)1 << (uint64_t)(mo_id - 1);
|
||||
DevAssert(ue->measurement_object_ids & mask);
|
||||
ue->measurement_object_ids &= ~mask;
|
||||
}
|
||||
|
||||
int allocate_measurement_id(void *_ue)
|
||||
{
|
||||
/* todo(?) use common/utils/collection/linear_alloc.h */
|
||||
gNB_RRC_UE_t *ue = _ue;
|
||||
uint64_t mask = 1;
|
||||
int ret;
|
||||
for (ret = 0; ret < 64; ret++, mask <<= 1)
|
||||
if (!(ue->measurement_ids & mask))
|
||||
break;
|
||||
DevAssert(ret < 64);
|
||||
ue->measurement_ids |= mask;
|
||||
return ret + 1;
|
||||
}
|
||||
|
||||
void free_measurement_id(void *_ue, int mo_id)
|
||||
{
|
||||
gNB_RRC_UE_t *ue = _ue;
|
||||
uint64_t mask = (uint64_t)1 << (uint64_t)(mo_id - 1);
|
||||
DevAssert(ue->measurement_ids & mask);
|
||||
ue->measurement_ids &= ~mask;
|
||||
}
|
||||
|
||||
int allocate_report_config_id(void *_ue)
|
||||
{
|
||||
/* todo(?) use common/utils/collection/linear_alloc.h */
|
||||
gNB_RRC_UE_t *ue = _ue;
|
||||
uint64_t mask = 1;
|
||||
int ret;
|
||||
for (ret = 0; ret < 64; ret++, mask <<= 1)
|
||||
if (!(ue->report_config_ids & mask))
|
||||
break;
|
||||
DevAssert(ret < 64);
|
||||
ue->report_config_ids |= mask;
|
||||
return ret + 1;
|
||||
}
|
||||
|
||||
void free_report_config_id(void *_ue, int mo_id)
|
||||
{
|
||||
gNB_RRC_UE_t *ue = _ue;
|
||||
uint64_t mask = (uint64_t)1 << (uint64_t)(mo_id - 1);
|
||||
DevAssert(ue->report_config_ids & mask);
|
||||
ue->report_config_ids &= ~mask;
|
||||
}
|
||||
|
||||
void reset_all_measurement_ids(void *_ue)
|
||||
{
|
||||
gNB_RRC_UE_t *ue = _ue;
|
||||
|
||||
ue->measurement_object_ids = 0;
|
||||
ue->measurement_ids = 0;
|
||||
ue->report_config_ids = 0;
|
||||
}
|
||||
25
openair2/RRC/NR/rrc_gNB_measurements.h
Normal file
25
openair2/RRC/NR/rrc_gNB_measurements.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
|
||||
*/
|
||||
|
||||
#ifndef RRC_GNB_MEASUREMENTS_H_
|
||||
#define RRC_GNB_MEASUREMENTS_H_
|
||||
|
||||
/* void *ue is actually gNB_RRC_UE_t *ue but this cannot be used because
|
||||
* including "openair2/RRC/NR/nr_rrc_defs.h" breaks the compilation of
|
||||
* nr-uesoftmodem
|
||||
* to be fixed if it's seen as a problem
|
||||
*/
|
||||
|
||||
int allocate_measurement_object_id(void *ue);
|
||||
void free_measurement_object_id(void *ue, int mo_id);
|
||||
|
||||
int allocate_measurement_id(void *ue);
|
||||
void free_measurement_id(void *ue, int mo_id);
|
||||
|
||||
int allocate_report_config_id(void *ue);
|
||||
void free_report_config_id(void *ue, int mo_id);
|
||||
|
||||
void reset_all_measurement_ids(void *ue);
|
||||
|
||||
#endif /* RRC_GNB_MEASUREMENTS_H_ */
|
||||
1488
openair2/RRC/NR/rrc_gNB_nrdc.c
Normal file
1488
openair2/RRC/NR/rrc_gNB_nrdc.c
Normal file
File diff suppressed because it is too large
Load Diff
38
openair2/RRC/NR/rrc_gNB_nrdc.h
Normal file
38
openair2/RRC/NR/rrc_gNB_nrdc.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
|
||||
*/
|
||||
|
||||
#ifndef __RRC_GNB_NRDC_H__
|
||||
#define __RRC_GNB_NRDC_H__
|
||||
|
||||
#include "nr_rrc_defs.h"
|
||||
#include "NR_DL-DCCH-Message.h"
|
||||
#include "NR_UL-DCCH-Message.h"
|
||||
|
||||
void rrc_gnb_nrdc_start(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue);
|
||||
void rrc_gnb_free_nrdc(gNB_RRC_UE_t *ue);
|
||||
void rrc_gnb_nrdc_ue_capabilities_received(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, const NR_UECapabilityInformation_t *ue_cap);
|
||||
void rrc_gnb_nrdc_rrc_reconfiguration_complete_received(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, int xid);
|
||||
void rrc_gnb_nrdc_measurement_received(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, NR_MeasurementReport_t *meas);
|
||||
void rrc_gnb_nrdc_timeout(gNB_RRC_INST *rrc, nr_rrc_nrdc_timeout_t *timeout);
|
||||
|
||||
int get_scg_measurement_id(gNB_RRC_UE_t *ue);
|
||||
|
||||
bool rrc_gnb_nrdc_wait_for_f1_context_setup_response(gNB_RRC_UE_t *ue);
|
||||
void nrdc_rrc_CU_process_ue_context_setup_response(gNB_RRC_UE_t *ue, gNB_RRC_INST *rrc, f1ap_ue_context_setup_resp_t *resp);
|
||||
|
||||
bool is_nrdc_bearer(gNB_RRC_UE_t *ue, int rb_id);
|
||||
void nrdc_release_bearer(const gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, int bearer_id);
|
||||
|
||||
bool rrc_gnb_nrdc_wait_for_f1_context_modification_response(gNB_RRC_UE_t *ue);
|
||||
void nrdc_rrc_CU_process_ue_context_modification_response(gNB_RRC_UE_t *ue, gNB_RRC_INST *rrc, f1ap_ue_context_mod_resp_t *resp);
|
||||
|
||||
bool nrdc_handle_f1_context_release_complete(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, f1ap_ue_context_rel_cplt_t *complete);
|
||||
|
||||
void nrdc_scg_ue_release(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue);
|
||||
|
||||
void nrdc_handle_scg_failure_information(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, NR_SCGFailureInformation_t *msg);
|
||||
bool rrc_gnb_nrdc_wait_for_scg_failure_context_modification_response(gNB_RRC_UE_t *ue);
|
||||
void nrdc_scg_failure_context_modification_response(gNB_RRC_UE_t *ue, gNB_RRC_INST *rrc, f1ap_ue_context_mod_resp_t *resp);
|
||||
|
||||
#endif /* __RRC_GNB_NRDC_H__ */
|
||||
@@ -941,7 +941,7 @@ static void nr_rrc_process_reconfigurationWithSync(NR_UE_RRC_INST_t *rrc,
|
||||
|
||||
// Clear neighbor cell lists from measurement objects during handover
|
||||
rrcPerNB_t *rrcNB = &rrc->perNB[gNB_index];
|
||||
for (int i = 0; i < MAX_MEAS_OBJ; i++) {
|
||||
for (int i = 0; i < NR_MAX_MEAS_OBJ; i++) {
|
||||
if (rrcNB->MeasObj[i] && rrcNB->MeasObj[i]->measObject.present == NR_MeasObjectToAddMod__measObject_PR_measObjectNR) {
|
||||
NR_MeasObjectNR_t *measObjNR = rrcNB->MeasObj[i]->measObject.choice.measObjectNR;
|
||||
if (measObjNR->cellsToAddModList) {
|
||||
@@ -1221,7 +1221,7 @@ static void handle_measobj_remove(rrcPerNB_t *rrc, struct NR_MeasObjectToRemoveL
|
||||
// remove the entry with the matching measObjectId from the measObjectList
|
||||
asn1cFreeStruc(asn_DEF_NR_MeasObjectToAddMod, rrc->MeasObj[id - 1]);
|
||||
// remove all measId associated with this measObjectId from the measIdList
|
||||
for (int j = 0; j < MAX_MEAS_ID; j++) {
|
||||
for (int j = 0; j < NR_MAX_MEAS_ID; j++) {
|
||||
if (rrc->MeasId[j] && rrc->MeasId[j]->measObjectId == id) {
|
||||
asn1cFreeStruc(asn_DEF_NR_MeasIdToAddMod, rrc->MeasId[j]);
|
||||
handle_meas_reporting_remove(rrc, j, timers);
|
||||
@@ -1319,7 +1319,7 @@ static void handle_reportconfig_remove(rrcPerNB_t *rrc,
|
||||
NR_ReportConfigId_t id = *remove_list->list.array[i];
|
||||
// remove the entry with the matching reportConfigId from the reportConfigList
|
||||
asn1cFreeStruc(asn_DEF_NR_ReportConfigToAddMod, rrc->ReportConfig[id]);
|
||||
for (int j = 0; j < MAX_MEAS_ID; j++) {
|
||||
for (int j = 0; j < NR_MAX_MEAS_ID; j++) {
|
||||
if (rrc->MeasId[j] && rrc->MeasId[j]->reportConfigId == id) {
|
||||
// remove all measId associated with the reportConfigId from the measIdList
|
||||
asn1cFreeStruc(asn_DEF_NR_MeasIdToAddMod, rrc->MeasId[j]);
|
||||
@@ -1341,7 +1341,7 @@ static void handle_reportconfig_addmod(rrcPerNB_t *rrc,
|
||||
}
|
||||
NR_ReportConfigId_t id = rep->reportConfigId;
|
||||
if (rrc->ReportConfig[id]) {
|
||||
for (int j = 0; j < MAX_MEAS_ID; j++) {
|
||||
for (int j = 0; j < NR_MAX_MEAS_ID; j++) {
|
||||
// for each measId associated with this reportConfigId included in the measIdList
|
||||
if (rrc->MeasId[j] && rrc->MeasId[j]->reportConfigId == id)
|
||||
handle_meas_reporting_remove(rrc, j, timers);
|
||||
@@ -1371,7 +1371,7 @@ static void handle_quantityconfig(rrcPerNB_t *rrc, NR_QuantityConfig_t *quantity
|
||||
UPDATE_IE(rrc->QuantityConfig[i]->quantityConfigRS_Index, quantityNR->quantityConfigRS_Index, struct NR_QuantityConfigRS);
|
||||
}
|
||||
}
|
||||
for (int j = 0; j < MAX_MEAS_ID; j++) {
|
||||
for (int j = 0; j < NR_MAX_MEAS_ID; j++) {
|
||||
// for each measId included in the measIdList
|
||||
if (rrc->MeasId[j])
|
||||
handle_meas_reporting_remove(rrc, j, timers);
|
||||
@@ -2777,7 +2777,7 @@ static int get_rsrp_value(const meas_t *cell)
|
||||
|
||||
static int get_meas_id(rrcPerNB_t *rrcNB, int report_config_id)
|
||||
{
|
||||
for (int j = 0; j < MAX_MEAS_ID; j++) {
|
||||
for (int j = 0; j < NR_MAX_MEAS_ID; j++) {
|
||||
NR_MeasIdToAddMod_t *meas_id_toAddMod = rrcNB->MeasId[j];
|
||||
if (meas_id_toAddMod && meas_id_toAddMod->reportConfigId == report_config_id)
|
||||
return meas_id_toAddMod->measId;
|
||||
@@ -2944,7 +2944,7 @@ static void nr_ue_check_meas_report(NR_UE_RRC_INST_t *rrc, const uint8_t gnb_ind
|
||||
rrcPerNB_t *rrcNB = rrc->perNB + gnb_index;
|
||||
l3_measurements_t *l3_measurements = &rrcNB->l3_measurements;
|
||||
|
||||
for (int i = 0; i < MAX_MEAS_CONFIG; i++) {
|
||||
for (int i = 0; i < NR_MAX_MEAS_CONFIG; i++) {
|
||||
NR_ReportConfigToAddMod_t *report_config = rrcNB->ReportConfig[i];
|
||||
if (report_config == NULL)
|
||||
continue;
|
||||
|
||||
@@ -42,9 +42,9 @@
|
||||
#include "notified_fifo.h"
|
||||
|
||||
#define NB_CNX_UE 2//MAX_MANAGED_RG_PER_MOBILE
|
||||
#define MAX_MEAS_OBJ 64
|
||||
#define MAX_MEAS_CONFIG 64
|
||||
#define MAX_MEAS_ID 64
|
||||
#define NR_MAX_MEAS_OBJ 64
|
||||
#define NR_MAX_MEAS_CONFIG 64
|
||||
#define NR_MAX_MEAS_ID 64
|
||||
#define MAX_QUANTITY_CONFIG 2
|
||||
#define NUMBER_OF_NEIGHBORING_CELLS_MAX 1
|
||||
|
||||
@@ -187,11 +187,11 @@ typedef struct l3_measurements_s {
|
||||
} l3_measurements_t;
|
||||
|
||||
typedef struct rrcPerNB {
|
||||
NR_MeasObjectToAddMod_t *MeasObj[MAX_MEAS_OBJ];
|
||||
NR_ReportConfigToAddMod_t *ReportConfig[MAX_MEAS_CONFIG];
|
||||
NR_MeasObjectToAddMod_t *MeasObj[NR_MAX_MEAS_OBJ];
|
||||
NR_ReportConfigToAddMod_t *ReportConfig[NR_MAX_MEAS_CONFIG];
|
||||
NR_QuantityConfigNR_t *QuantityConfig[MAX_QUANTITY_CONFIG];
|
||||
NR_MeasIdToAddMod_t *MeasId[MAX_MEAS_ID];
|
||||
NR_VarMeasReport_t *MeasReport[MAX_MEAS_ID];
|
||||
NR_MeasIdToAddMod_t *MeasId[NR_MAX_MEAS_ID];
|
||||
NR_VarMeasReport_t *MeasReport[NR_MAX_MEAS_ID];
|
||||
NR_MeasGapConfig_t *measGapConfig;
|
||||
NR_UE_RRC_SI_INFO SInfo;
|
||||
NR_RSRP_Range_t s_measure;
|
||||
|
||||
Reference in New Issue
Block a user