From 5785bc18949cd1677a4b7f7d00c529bec3a61e5d Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 25 Nov 2024 18:55:47 +0100 Subject: [PATCH 01/32] Msg4 RNTI not found: abort RA --- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c index 29e4ea1122..c7faf78179 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c @@ -1941,7 +1941,8 @@ static void nr_generate_Msg4_MsgB(module_id_t module_idP, NR_UE_info_t *UE = find_nr_UE(&nr_mac->UE_info, ra->rnti); if (!UE) { - LOG_E(NR_MAC, "want to generate %s, but rnti %04x not in the table\n", ra_type_str, ra->rnti); + LOG_E(NR_MAC, "want to generate %s, but rnti %04x not in the table. Abort RA\n", ra_type_str, ra->rnti); + nr_clear_ra_proc(ra); return; } From c80821e5784e444ae45a2af2cce94aa49dc49669 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 5 Oct 2023 08:52:48 +0200 Subject: [PATCH 02/32] NR MAC: save total/used PRBs aggregate --- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 5 +++++ openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h | 7 +++++++ 2 files changed, 12 insertions(+) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index 55c86594cd..730e954f4b 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -938,6 +938,9 @@ void nr_schedule_ue_spec(module_id_t module_id, NR_UEs_t *UE_info = &gNB_mac->UE_info; nfapi_nr_dl_tti_request_body_t *dl_req = &DL_req->dl_tti_request_body; + const NR_BWP_t *initialDL = &scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters; + gNB_mac->mac_stats.total_prb_aggregate += NRRIV2BW(initialDL->locationAndBandwidth, MAX_BWP_SIZE); + UE_iterator(UE_info->list, UE) { NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl; NR_UE_DL_BWP_t *current_BWP = &UE->current_DL_BWP; @@ -1252,6 +1255,7 @@ void nr_schedule_ue_spec(module_id_t module_id, T(T_GNB_MAC_RETRANSMISSION_DL_PDU_WITH_DATA, T_INT(module_id), T_INT(CC_id), T_INT(rnti), T_INT(frame), T_INT(slot), T_INT(current_harq_pid), T_INT(harq->round), T_BUFFER(harq->transportBlock, TBS)); UE->mac_stats.dl.total_rbs_retx += sched_pdsch->rbSize; + gNB_mac->mac_stats.used_prb_aggregate += sched_pdsch->rbSize; } else { /* initial transmission */ LOG_D(NR_MAC, "Initial HARQ transmission in %d.%d\n", frame, slot); uint8_t *buf = (uint8_t *) harq->transportBlock; @@ -1369,6 +1373,7 @@ void nr_schedule_ue_spec(module_id_t module_id, UE->mac_stats.dl.num_mac_sdu += sdus; UE->mac_stats.dl.current_rbs = sched_pdsch->rbSize; UE->mac_stats.dl.total_sdu_bytes += dlsch_total_bytes; + gNB_mac->mac_stats.used_prb_aggregate += sched_pdsch->rbSize; /* save retransmission information */ harq->sched_pdsch = *sched_pdsch; diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h index 420445076f..641f6d93d1 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h +++ b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h @@ -807,6 +807,11 @@ typedef struct { int8_t nvipc_poll_core; } nvipc_params_t; +typedef struct { + uint64_t total_prb_aggregate; + uint64_t used_prb_aggregate; +} mac_stats_t; + /*! \brief top level eNB MAC structure */ typedef struct gNB_MAC_INST_s { /// Ethernet parameters for northbound midhaul interface @@ -930,6 +935,8 @@ typedef struct gNB_MAC_INST_s { pthread_mutex_t sched_lock; + mac_stats_t mac_stats; + } gNB_MAC_INST; #endif /*__LAYER2_NR_MAC_GNB_H__ */ From f804a355ad34a5131afe661dedd9cc6a8e89f08a Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 8 Feb 2024 10:46:19 +0100 Subject: [PATCH 03/32] Trigger F1 UE context release if no UE context in RRC --- executables/nr-cuup.c | 3 +++ openair2/RRC/NR/rrc_gNB.c | 43 ++++++++++++++++++++++++--------------- 2 files changed, 30 insertions(+), 16 deletions(-) diff --git a/executables/nr-cuup.c b/executables/nr-cuup.c index 381e56bd2f..978486f261 100644 --- a/executables/nr-cuup.c +++ b/executables/nr-cuup.c @@ -145,6 +145,9 @@ f1ap_cudu_inst_t *getCxt(instance_t instanceP) return &fake; } configmodule_interface_t *uniqCfg = NULL; + +void rrc_gNB_send_NGAP_UE_CONTEXT_RELEASE_COMPLETE(instance_t instance, uint32_t gNB_ue_ngap_id) { }; + int main(int argc, char **argv) { /// static configuration for NR at the moment diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index e8c7fcdc2c..9bf178afe2 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -106,6 +106,21 @@ extern RAN_CONTEXT_t RC; mui_t rrc_gNB_mui = 0; +typedef struct deliver_ue_ctxt_release_data_t { + gNB_RRC_INST *rrc; + f1ap_ue_context_release_cmd_t *release_cmd; + sctp_assoc_t assoc_id; +} deliver_ue_ctxt_release_data_t; + +static void rrc_deliver_ue_ctxt_release_cmd(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_release_data_t *data = deliver_pdu_data; + data->release_cmd->rrc_container = (uint8_t*) buf; + data->release_cmd->rrc_container_length = size; + data->rrc->mac_rrc.ue_context_release_command(data->assoc_id, data->release_cmd); +} + ///---------------------------------------------------------------------------------------------------------------/// ///---------------------------------------------------------------------------------------------------------------/// @@ -1974,8 +1989,18 @@ static void rrc_CU_process_ue_context_release_request(MessageDef *msg_p, sctp_as 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 + LOG_W(RRC, "could not find UE context for CU UE ID %u: auto-generate release command\n", req->gNB_CU_ue_id); + uint8_t buffer[RRC_BUF_SIZE] = {0}; + int size = do_NR_RRCRelease(buffer, RRC_BUF_SIZE, rrc_gNB_get_next_transaction_identifier(0)); + f1ap_ue_context_release_cmd_t ue_context_release_cmd = { + .gNB_CU_ue_id = req->gNB_CU_ue_id, + .gNB_DU_ue_id = req->gNB_DU_ue_id, + .cause = F1AP_CAUSE_RADIO_NETWORK, + .cause_value = 10, // 10 = F1AP_CauseRadioNetwork_normal_release + .srb_id = DCCH, + }; + deliver_ue_ctxt_release_data_t data = {.rrc = rrc, .release_cmd = &ue_context_release_cmd}; + nr_pdcp_data_req_srb(req->gNB_CU_ue_id, DCCH, rrc_gNB_mui++, size, buffer, rrc_deliver_ue_ctxt_release_cmd, &data); return; } @@ -2645,20 +2670,6 @@ void rrc_gNB_generate_SecurityModeCommand(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue_p) nr_rrc_transfer_protected_rrc_message(rrc, ue_p, DL_SCH_LCID_DCCH, buffer, size); } -typedef struct deliver_ue_ctxt_release_data_t { - gNB_RRC_INST *rrc; - f1ap_ue_context_release_cmd_t *release_cmd; - sctp_assoc_t assoc_id; -} deliver_ue_ctxt_release_data_t; -static void rrc_deliver_ue_ctxt_release_cmd(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_release_data_t *data = deliver_pdu_data; - data->release_cmd->rrc_container = (uint8_t*) buf; - data->release_cmd->rrc_container_length = size; - data->rrc->mac_rrc.ue_context_release_command(data->assoc_id, data->release_cmd); -} - //----------------------------------------------------------------------------- /* * Generate the RRC Connection Release to UE. From 6330bfe0e8c591f097747b6205a670ad5ab7f7e5 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 26 Nov 2024 07:57:55 +0100 Subject: [PATCH 04/32] fixup: there is no init_gNB_afterRU() --- executables/nr-softmodem.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/executables/nr-softmodem.h b/executables/nr-softmodem.h index 4311777f6c..ecbf5d8759 100644 --- a/executables/nr-softmodem.h +++ b/executables/nr-softmodem.h @@ -52,7 +52,7 @@ extern void stop_RU(int nb_ru); extern void kill_NR_RU_proc(int inst); extern void set_function_spec_param(RU_t *ru); -void init_gNB_afterRU(void); +void init_eNB_afterRU(void); void init_pdcp(void); From a7d0e609ed954c58b3bd3b2708bb28fb2a7cf80f Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 27 Nov 2024 16:58:00 +0100 Subject: [PATCH 05/32] config module: check that memory is enough, allow more restarts A later commit implements the "restart" of the L1. The L1 reads configuration options. First, allow to re-read configurations by increasing the amount of memory the config module might allocate. Then, avoid an out-of-bound write by checking that we still have memory available (and assert if not possible). --- common/config/config_common.c | 1 + common/config/config_load_configmodule.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/common/config/config_common.c b/common/config/config_common.c index d6e499b6a8..2bca71d912 100644 --- a/common/config/config_common.c +++ b/common/config/config_common.c @@ -108,6 +108,7 @@ void *config_allocate_new(configmodule_interface_t *cfg, int sz, bool autoFree) // add the memory piece in the managed memory pieces list pthread_mutex_lock(&cfg->memBlocks_mutex); int newBlockIdx=cfg->numptrs++; + AssertFatal(newBlockIdx < sizeofArray(cfg->oneBlock), "reached maximum number of dynamically allocatable blocks\n"); oneBlock_t* tmp=&cfg->oneBlock[newBlockIdx]; tmp->ptrs = (char *)ptr; tmp->ptrsAllocated = true; diff --git a/common/config/config_load_configmodule.h b/common/config/config_load_configmodule.h index bc38322805..3ec27d456f 100644 --- a/common/config/config_load_configmodule.h +++ b/common/config/config_load_configmodule.h @@ -41,7 +41,7 @@ #include "common/config/config_paramdesc.h" #include "common/utils/T/T.h" #define CONFIG_MAX_OOPT_PARAMS 10 // maximum number of parameters in the -O option (-O :P1:P2... -#define CONFIG_MAX_ALLOCATEDPTRS 2048 // maximum number of parameters that can be dynamicaly allocated in the config module +#define CONFIG_MAX_ALLOCATEDPTRS 32768 // maximum number of parameters that can be dynamicaly allocated in the config module /* default values for configuration module parameters */ #define CONFIG_LIBCONFIGFILE "libconfig" // use libconfig file From 3361db1a344515c88aa4e28494c0fbd4e1dea307 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 28 Nov 2024 15:16:53 +0100 Subject: [PATCH 06/32] Stop RU thread pool on exit, if applicable --- executables/nr-ru.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/executables/nr-ru.c b/executables/nr-ru.c index dd31088ff8..37e520431c 100644 --- a/executables/nr-ru.c +++ b/executables/nr-ru.c @@ -1443,6 +1443,12 @@ void kill_NR_RU_proc(int inst) { RU_t *ru = RC.ru[inst]; RU_proc_t *proc = &ru->proc; + if (ru->if_south != REMOTE_IF4p5) { + abortTpool(ru->threadPool); + abortNotifiedFIFO(ru->respfeprx); + abortNotifiedFIFO(ru->respfeptx); + } + /* Note: it seems pthread_FH and and FEP thread below both use * mutex_fep/cond_fep. Thus, we unlocked above for pthread_FH above and do * the same for FEP thread below again (using broadcast() to ensure both From 70b461100179ef14396cdcb2ae8f84ed0037b9c4 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 28 Nov 2024 14:42:00 +0100 Subject: [PATCH 07/32] Stop L1_rx_thread/L1_tx_thread first These are the "main" worker threads (apart the ru_thread). If the thread-pool stops, they might get stuck. E.g., in PUSCH channel estimation, it uses a local response queue, and if the thread pool is terminated, it seems that the L1_rx_thread can get stuck in there. To limit these problems, attempt to stop these threads first; stop the queue from which they read and make them terminate; then, stop the thread pool. Also, fix the termination of respPuschSymb queue. --- executables/nr-gnb.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/executables/nr-gnb.c b/executables/nr-gnb.c index 4206cfd585..48c5aca630 100644 --- a/executables/nr-gnb.c +++ b/executables/nr-gnb.c @@ -383,12 +383,16 @@ void init_gNB_Tpool(int inst) { void term_gNB_Tpool(int inst) { PHY_VARS_gNB *gNB = RC.gNB[inst]; - abortTpool(&gNB->threadPool); - abortNotifiedFIFO(&gNB->respDecode); abortNotifiedFIFO(&gNB->resp_L1); + pthread_join(gNB->L1_rx_thread, NULL); + abortNotifiedFIFO(&gNB->L1_tx_out); + pthread_join(gNB->L1_tx_thread, NULL); + + abortTpool(&gNB->threadPool); + abortNotifiedFIFO(&gNB->respPuschSymb); + abortNotifiedFIFO(&gNB->respDecode); abortNotifiedFIFO(&gNB->L1_tx_free); abortNotifiedFIFO(&gNB->L1_tx_filled); - abortNotifiedFIFO(&gNB->L1_tx_out); abortNotifiedFIFO(&gNB->L1_rx_out); gNB_L1_proc_t *proc = &gNB->proc; From 5600316d343da3f2bafa8dee24ca72c6e94d2030 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 3 Dec 2024 12:18:47 +0100 Subject: [PATCH 08/32] UL ref sequences: prevent double-initialization The existing code prevents double initialization if e.g., both gNB&UE call these functions (like in a simulator). However, in the case of a double-initialization such as stopping and re-starting the L1, the memory will be freed, but not freed again. Basically, this works: generate() generate() # not initialized again but this leads to problems: generate() free() generate() # not initialized again, but should be! Reimplement the check based on the data to be initialized, such that an intermediate free() will be handled properly. --- openair1/PHY/NR_REFSIG/ul_ref_seq_nr.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/openair1/PHY/NR_REFSIG/ul_ref_seq_nr.c b/openair1/PHY/NR_REFSIG/ul_ref_seq_nr.c index 22fe1b0d13..baa2734f87 100644 --- a/openair1/PHY/NR_REFSIG/ul_ref_seq_nr.c +++ b/openair1/PHY/NR_REFSIG/ul_ref_seq_nr.c @@ -177,10 +177,9 @@ c16_t *gNB_dmrs_lowpaprtype1_sequence[U_GROUP_NUMBER][V_BASE_SEQUENCE_NUMBER][MA void generate_lowpapr_typ1_refsig_sequences(unsigned int scaling) { /* prevent multiple calls, relevant when both UE & gNB initialize this */ - static bool already_called = false; + bool already_called = gNB_dmrs_lowpaprtype1_sequence[0][0][0] != NULL; if (already_called) return; - already_called = true; unsigned int v = 0; // sequence hopping and group hopping are not supported yet for (unsigned int Msc_RS = 0; Msc_RS <= INDEX_SB_LESS_32; Msc_RS++) { @@ -200,9 +199,8 @@ c16_t *dmrs_lowpaprtype1_ul_ref_sig[U_GROUP_NUMBER][V_BASE_SEQUENCE_NUMBER][MAX_ void generate_ul_reference_signal_sequences(unsigned int scaling) { /* prevent multiple calls, relevant when both UE & gNB initialize this */ - static bool already_called = false; + bool already_called = rv_ul_ref_sig[0][0][0] != NULL; if (already_called) return; - already_called = true; unsigned int u,v,Msc_RS; From 462d1a6a8b17af5ebe69b430f0341ceffa263b9d Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 14 Feb 2024 10:24:06 +0100 Subject: [PATCH 09/32] RLF: avoid encoder problem --- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index e0cc7a8fe0..1f9cb9c0c1 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -3319,7 +3319,7 @@ bool nr_mac_request_release_ue(const gNB_MAC_INST *nrmac, int rnti) .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, + .cause_value = F1AP_CauseRadioNetwork_rl_failure_rlc, }; nrmac->mac_rrc.ue_context_release_request(&request); return true; From bc8556eafb819ac0ff6efb1f5d59d15f424babcd Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 27 Nov 2024 16:51:13 +0100 Subject: [PATCH 10/32] Implement RFsim stopServer: close listen socket, free memory --- radio/rfsimulator/simulator.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/radio/rfsimulator/simulator.c b/radio/rfsimulator/simulator.c index d362d5ef7b..faa185a0cb 100644 --- a/radio/rfsimulator/simulator.c +++ b/radio/rfsimulator/simulator.c @@ -1085,7 +1085,16 @@ static void rfsimulator_end(openair0_device *device) { } close(s->epollfd); hashtable_destroy(&s->fd_to_buf_map); + free(s); } +static void stopServer(openair0_device *device) +{ + rfsimulator_state_t *t = (rfsimulator_state_t *) device->priv; + DevAssert(t != NULL); + close(t->listen_sock); + rfsimulator_end(device); +} + static int rfsimulator_stop(openair0_device *device) { return 0; } @@ -1136,7 +1145,7 @@ int device_init(openair0_device *device, openair0_config_t *openair0_cfg) { device->trx_start_func = rfsimulator->role == SIMU_ROLE_SERVER ? startServer : startClient; device->trx_get_stats_func = rfsimulator_get_stats; device->trx_reset_stats_func = rfsimulator_reset_stats; - device->trx_end_func = rfsimulator_end; + device->trx_end_func = rfsimulator->role == SIMU_ROLE_SERVER ? stopServer : rfsimulator_end; device->trx_stop_func = rfsimulator_stop; device->trx_set_freq_func = rfsimulator_set_freq; device->trx_set_gains_func = rfsimulator_set_gains; From 3c94b9a18f298ead25ae8b8b99ed104d57ffbda3 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 26 Nov 2024 11:57:00 +0100 Subject: [PATCH 11/32] rrc_gNB_du.c: avoid potential double-free Set pointer to NULL when freeing to avoid potential double frees. --- openair2/RRC/NR/rrc_gNB_du.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openair2/RRC/NR/rrc_gNB_du.c b/openair2/RRC/NR/rrc_gNB_du.c index bc3c564516..4f563d8c43 100644 --- a/openair2/RRC/NR/rrc_gNB_du.c +++ b/openair2/RRC/NR/rrc_gNB_du.c @@ -505,8 +505,10 @@ void rrc_gNB_process_f1_du_configuration_update(f1ap_gnb_du_configuration_update // MIB is mandatory, so will be overwritten. SIB1 is optional, so will // only be overwritten if present in sys_info ASN_STRUCT_FREE(asn_DEF_NR_MIB, du->mib); - if (sys_info->sib1 != NULL) + if (sys_info->sib1 != NULL) { ASN_STRUCT_FREE(asn_DEF_NR_SIB1, du->sib1); + du->sib1 = NULL; + } NR_MIB_t *mib = NULL; if (!extract_sys_info(sys_info, &mib, &du->sib1)) { From c852d9337cb67ff150de99c305feb41b0963c670 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 27 Nov 2024 16:59:09 +0100 Subject: [PATCH 12/32] nr_rrc_config_ul_tda(): ensure not double initialization For the "restart" feature, earlier versions where creating multiple UL TDA. This is not a problem (anymore), but to avoid that we call this multiple times, add this assert. --- openair2/RRC/NR/nr_rrc_config.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/openair2/RRC/NR/nr_rrc_config.c b/openair2/RRC/NR/nr_rrc_config.c index e70d7b66eb..f6e7c15e8b 100644 --- a/openair2/RRC/NR/nr_rrc_config.c +++ b/openair2/RRC/NR/nr_rrc_config.c @@ -920,10 +920,14 @@ void nr_rrc_config_dl_tda(struct NR_PDSCH_TimeDomainResourceAllocationList *pdsc } -void nr_rrc_config_ul_tda(NR_ServingCellConfigCommon_t *scc, int min_fb_delay){ - +void nr_rrc_config_ul_tda(NR_ServingCellConfigCommon_t *scc, int min_fb_delay) +{ //TODO change to accomodate for SRS + const NR_PUSCH_TimeDomainResourceAllocationList_t *tda = + scc->uplinkConfigCommon->initialUplinkBWP->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList; + AssertFatal(tda->list.count == 0, "already have pusch_TimeDomainAllocationList members\n"); + frame_type_t frame_type = get_frame_type(*scc->downlinkConfigCommon->frequencyInfoDL->frequencyBandList.list.array[0], *scc->ssbSubcarrierSpacing); const int k2 = min_fb_delay; From ed876a53b291d0fe4130c5d46154eddbe7e48289 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 27 Nov 2024 18:27:14 +0100 Subject: [PATCH 13/32] F1: refactor out served_cell_info/du system info Split out these functions, to be reused in a follow-up commit to correctly have gNB-DU configuration update message working. Change the return type copy_f1ap_served_cell_info(), to return the copy, which IMO is more intuitive then passing target and source in parentheses. --- openair2/F1AP/lib/f1ap_interface_management.c | 76 ++++++++++++------- openair2/F1AP/lib/f1ap_interface_management.h | 2 +- openair2/RRC/NR/rrc_gNB_du.c | 2 +- 3 files changed, 50 insertions(+), 30 deletions(-) diff --git a/openair2/F1AP/lib/f1ap_interface_management.c b/openair2/F1AP/lib/f1ap_interface_management.c index f067985a2d..a8d46787b4 100644 --- a/openair2/F1AP/lib/f1ap_interface_management.c +++ b/openair2/F1AP/lib/f1ap_interface_management.c @@ -534,20 +534,56 @@ bool decode_f1ap_setup_request(const F1AP_F1AP_PDU_t *pdu, f1ap_setup_req_t *out return true; } -void copy_f1ap_served_cell_info(f1ap_served_cell_info_t *dest, const f1ap_served_cell_info_t *src) +f1ap_served_cell_info_t copy_f1ap_served_cell_info(const f1ap_served_cell_info_t *src) { - // shallow copy - *dest = *src; - // tac + f1ap_served_cell_info_t dst = { + .plmn = src->plmn, + .nr_cellid = src->nr_cellid, + .nr_pci = src->nr_pci, + .num_ssi = src->num_ssi, + .mode = src->mode, + }; + + for (int i = 0; i < src->num_ssi; ++i) + dst.nssai[i] = src->nssai[i]; + + if (src->mode == F1AP_MODE_TDD) + dst.tdd = src->tdd; + else + dst.fdd = src->fdd; + if (src->tac) { - dest->tac = malloc_or_fail(sizeof(*dest->tac)); - *dest->tac = *src->tac; + dst.tac = malloc_or_fail(sizeof(*dst.tac)); + *dst.tac = *src->tac; } - // measurement timing config - if (src->measurement_timing_config_len) { - dest->measurement_timing_config = calloc_or_fail(src->measurement_timing_config_len, sizeof(*dest->measurement_timing_config)); - memcpy(dest->measurement_timing_config, src->measurement_timing_config, src->measurement_timing_config_len); + + if (src->measurement_timing_config_len > 0) { + dst.measurement_timing_config_len = src->measurement_timing_config_len; + dst.measurement_timing_config = calloc_or_fail(src->measurement_timing_config_len, sizeof(*dst.measurement_timing_config)); + memcpy(dst.measurement_timing_config, src->measurement_timing_config, src->measurement_timing_config_len); } + return dst; +} + +static f1ap_gnb_du_system_info_t *copy_f1ap_gnb_du_system_info(const f1ap_gnb_du_system_info_t *src) +{ + if (!src) + return NULL; + + f1ap_gnb_du_system_info_t *dst = calloc_or_fail(1, sizeof(*dst)); + if (src->mib_length > 0) { + dst->mib_length = src->mib_length; + dst->mib = calloc_or_fail(src->mib_length, sizeof(*src->mib)); + memcpy(dst->mib, src->mib, dst->mib_length); + } + + if (src->sib1_length > 0) { + dst->sib1_length = src->sib1_length; + dst->sib1 = calloc_or_fail(src->sib1_length, sizeof(*dst->sib1)); + memcpy(dst->sib1, src->sib1, dst->sib1_length); + } + + return dst; } /** @@ -567,25 +603,9 @@ f1ap_setup_req_t cp_f1ap_setup_request(const f1ap_setup_req_t *msg) cp.num_cells_available = msg->num_cells_available; for (int n = 0; n < msg->num_cells_available; n++) { /* cell.info */ - f1ap_served_cell_info_t *sci = &cp.cell[n].info; - const f1ap_served_cell_info_t *msg_sci = &msg->cell[n].info; - copy_f1ap_served_cell_info(sci, msg_sci); + cp.cell[n].info = copy_f1ap_served_cell_info(&msg->cell[n].info); /* cell.sys_info */ - if (msg->cell[n].sys_info) { - f1ap_gnb_du_system_info_t *orig_sys_info = msg->cell[n].sys_info; - f1ap_gnb_du_system_info_t *copy_sys_info = calloc_or_fail(1, sizeof(*copy_sys_info)); - cp.cell[n].sys_info = copy_sys_info; - if (orig_sys_info->mib_length > 0) { - copy_sys_info->mib = calloc_or_fail(orig_sys_info->mib_length, sizeof(*copy_sys_info->mib)); - copy_sys_info->mib_length = orig_sys_info->mib_length; - memcpy(copy_sys_info->mib, orig_sys_info->mib, copy_sys_info->mib_length); - } - if (orig_sys_info->sib1_length > 0) { - copy_sys_info->sib1 = calloc_or_fail(orig_sys_info->sib1_length, sizeof(*copy_sys_info->sib1)); - copy_sys_info->sib1_length = orig_sys_info->sib1_length; - memcpy(copy_sys_info->sib1, orig_sys_info->sib1, copy_sys_info->sib1_length); - } - } + cp.cell[n].sys_info = copy_f1ap_gnb_du_system_info(msg->cell[n].sys_info); } for (int i = 0; i < sizeofArray(msg->rrc_ver); i++) cp.rrc_ver[i] = msg->rrc_ver[i]; diff --git a/openair2/F1AP/lib/f1ap_interface_management.h b/openair2/F1AP/lib/f1ap_interface_management.h index e483a74301..6fd79b4007 100644 --- a/openair2/F1AP/lib/f1ap_interface_management.h +++ b/openair2/F1AP/lib/f1ap_interface_management.h @@ -32,7 +32,7 @@ bool decode_f1ap_setup_request(const struct F1AP_F1AP_PDU *pdu, f1ap_setup_req_t f1ap_setup_req_t cp_f1ap_setup_request(const f1ap_setup_req_t *msg); bool eq_f1ap_setup_request(const f1ap_setup_req_t *a, const f1ap_setup_req_t *b); void free_f1ap_setup_request(const f1ap_setup_req_t *msg); -void copy_f1ap_served_cell_info(f1ap_served_cell_info_t *a, const f1ap_served_cell_info_t *b); +f1ap_served_cell_info_t copy_f1ap_served_cell_info(const f1ap_served_cell_info_t *src); void free_f1ap_cell(const f1ap_served_cell_info_t *info, const f1ap_gnb_du_system_info_t *sys_info); /* F1 Setup Response */ diff --git a/openair2/RRC/NR/rrc_gNB_du.c b/openair2/RRC/NR/rrc_gNB_du.c index 4f563d8c43..fc2aec64b4 100644 --- a/openair2/RRC/NR/rrc_gNB_du.c +++ b/openair2/RRC/NR/rrc_gNB_du.c @@ -448,7 +448,7 @@ static void update_cell_info(nr_rrc_du_container_t *du, const f1ap_served_cell_i f1ap_served_cell_info_t *ci = &du->setup_req->cell[0].info; // make sure no memory is allocated free_f1ap_cell(ci, NULL); - copy_f1ap_served_cell_info(ci, new_ci); + *ci = copy_f1ap_served_cell_info(new_ci); NR_MeasurementTimingConfiguration_t *new_mtc = extract_mtc(new_ci->measurement_timing_config, new_ci->measurement_timing_config_len); From 6b7055bb3750331616aaeb986b380dc8d050f525 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 27 Nov 2024 18:34:57 +0100 Subject: [PATCH 14/32] eq_f1ap_sys_info(): correct check a and b can only be equal if both NULL or both set (and all fields the same). Check for this. --- openair2/F1AP/lib/f1ap_lib_common.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/openair2/F1AP/lib/f1ap_lib_common.c b/openair2/F1AP/lib/f1ap_lib_common.c index 2938898aff..2dc149a8be 100644 --- a/openair2/F1AP/lib/f1ap_lib_common.c +++ b/openair2/F1AP/lib/f1ap_lib_common.c @@ -81,6 +81,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) { + if (!a && !b) + return true; + + /* will fail if not both a/b NULL or set */ + if ((!a) ^ (!b)) + return false; + /* MIB */ _F1_EQ_CHECK_INT(a->mib_length, b->mib_length); for (int i = 0; i < a->mib_length; i++) From 3de8a746fe91989f66f040a0f349f9926ab8cb51 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 28 Nov 2024 08:53:35 +0100 Subject: [PATCH 15/32] Fix eq_f1ap_cell_info(): correct TAC comparison Check that both a->tac/b->tac are present, or both not present. Then, check they are equal if present. --- openair2/F1AP/lib/f1ap_lib_common.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openair2/F1AP/lib/f1ap_lib_common.c b/openair2/F1AP/lib/f1ap_lib_common.c index 2dc149a8be..a569146815 100644 --- a/openair2/F1AP/lib/f1ap_lib_common.c +++ b/openair2/F1AP/lib/f1ap_lib_common.c @@ -53,7 +53,10 @@ bool eq_f1ap_cell_info(const f1ap_served_cell_info_t *a, const f1ap_served_cell_ { _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); + if ((!a->tac) ^ (!b->tac)) + return false; + if (a->tac) + _F1_EQ_CHECK_INT(*a->tac, *b->tac); _F1_EQ_CHECK_INT(a->mode, b->mode); if (a->mode == F1AP_MODE_TDD) { /* TDD */ From 509cc8cc584f137b6c3416b807e6ec3e00abdfbe Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 28 Nov 2024 08:32:52 +0100 Subject: [PATCH 16/32] Use PLMNID_TO_MCC_MNC() to decode PLMN ID --- openair2/F1AP/lib/f1ap_interface_management.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/openair2/F1AP/lib/f1ap_interface_management.c b/openair2/F1AP/lib/f1ap_interface_management.c index a8d46787b4..d44b230df6 100644 --- a/openair2/F1AP/lib/f1ap_interface_management.c +++ b/openair2/F1AP/lib/f1ap_interface_management.c @@ -173,7 +173,7 @@ static bool decode_served_cell_info(const F1AP_Served_Cell_Information_t *in, f1 OCTET_STRING_TO_INT24(in->fiveGS_TAC, *info->tac); } // NR CGI (M) - TBCD_TO_MCC_MNC(&(in->nRCGI.pLMN_Identity), info->plmn.mcc, info->plmn.mnc, info->plmn.mnc_digit_length); + PLMNID_TO_MCC_MNC(&(in->nRCGI.pLMN_Identity), info->plmn.mcc, info->plmn.mnc, info->plmn.mnc_digit_length); // NR Cell Identity (M) BIT_STRING_TO_NR_CELL_IDENTITY(&in->nRCGI.nRCellIdentity, info->nr_cellid); // NR PCI (M) @@ -310,7 +310,7 @@ static bool decode_cells_to_activate(served_cells_to_activate_t *out, const F1AP "in->value.present != F1AP_Cells_to_be_Activated_List_ItemIEs__value_PR_Cells_to_be_Activated_List_Item\n"); const F1AP_Cells_to_be_Activated_List_Item_t *cell = &in->value.choice.Cells_to_be_Activated_List_Item; // NR CGI (M) - TBCD_TO_MCC_MNC(&cell->nRCGI.pLMN_Identity, out->plmn.mcc, out->plmn.mnc, out->plmn.mnc_digit_length); + PLMNID_TO_MCC_MNC(&cell->nRCGI.pLMN_Identity, out->plmn.mcc, out->plmn.mnc, out->plmn.mnc_digit_length); BIT_STRING_TO_NR_CELL_IDENTITY(&cell->nRCGI.nRCellIdentity, out->nr_cellid); // NR PCI (O) if (cell->nRPCI != NULL) @@ -1162,7 +1162,7 @@ bool decode_f1ap_du_configuration_update(const F1AP_F1AP_PDU_t *pdu, f1ap_gnb_du /* Old NR CGI (M) */ F1AP_NRCGI_t *oldNRCGI = &served_cells_item->oldNRCGI; f1ap_plmn_t *old_plmn = &out->cell_to_modify[i].old_plmn; - TBCD_TO_MCC_MNC(&(oldNRCGI->pLMN_Identity), old_plmn->mcc, old_plmn->mnc, old_plmn->mnc_digit_length); + PLMNID_TO_MCC_MNC(&oldNRCGI->pLMN_Identity, old_plmn->mcc, old_plmn->mnc, old_plmn->mnc_digit_length); /* Old NR CGI Cell ID */ BIT_STRING_TO_NR_CELL_IDENTITY(&oldNRCGI->nRCellIdentity, out->cell_to_modify[i].old_nr_cellid); /* Served Cell Information (M) */ @@ -1186,7 +1186,7 @@ bool decode_f1ap_du_configuration_update(const F1AP_F1AP_PDU_t *pdu, f1ap_gnb_du F1AP_NRCGI_t *oldNRCGI = &served_cells_item->oldNRCGI; f1ap_plmn_t *plmn = &out->cell_to_delete[i].plmn; /* Old NR CGI (M) */ - TBCD_TO_MCC_MNC(&(oldNRCGI->pLMN_Identity), plmn->mcc, plmn->mnc, plmn->mnc_digit_length); + PLMNID_TO_MCC_MNC(&(oldNRCGI->pLMN_Identity), plmn->mcc, plmn->mnc, plmn->mnc_digit_length); // NR cellID BIT_STRING_TO_NR_CELL_IDENTITY(&oldNRCGI->nRCellIdentity, out->cell_to_delete[i].nr_cellid); } @@ -1541,7 +1541,7 @@ bool decode_f1ap_cu_configuration_update_acknowledge(const F1AP_F1AP_PDU_t *pdu, const F1AP_Cells_Failed_to_be_Activated_List_Item_t *item = &itemIE->value.choice.Cells_Failed_to_be_Activated_List_Item; // NR CGI (M) f1ap_plmn_t *plmn = &out->cells_failed_to_be_activated[j].plmn; - TBCD_TO_MCC_MNC(&(item->nRCGI.pLMN_Identity), plmn->mcc, plmn->mnc, plmn->mnc_digit_length); + PLMNID_TO_MCC_MNC(&(item->nRCGI.pLMN_Identity), plmn->mcc, plmn->mnc, plmn->mnc_digit_length); BIT_STRING_TO_NR_CELL_IDENTITY(&item->nRCGI.nRCellIdentity, out->cells_failed_to_be_activated[j].nr_cellid); // Cause (M) switch (item->cause.present) { From adbbbaeaba512edb2cab93a9baf363232dfb15b0 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 27 Nov 2024 19:50:11 +0100 Subject: [PATCH 17/32] Properly implement eq/cp() functions of gNB-DU config update, fix test Implement those functions following more closely 38.473, i.e., check all mandatory fields. Add mandatory fields in the test. --- openair2/F1AP/lib/f1ap_interface_management.c | 48 ++++++------------- openair2/F1AP/tests/f1ap_lib_test.c | 4 ++ 2 files changed, 19 insertions(+), 33 deletions(-) diff --git a/openair2/F1AP/lib/f1ap_interface_management.c b/openair2/F1AP/lib/f1ap_interface_management.c index d44b230df6..777a4e33a9 100644 --- a/openair2/F1AP/lib/f1ap_interface_management.c +++ b/openair2/F1AP/lib/f1ap_interface_management.c @@ -1236,10 +1236,8 @@ bool eq_f1ap_du_configuration_update(const f1ap_gnb_du_configuration_update_t *a for (int i = 0; i < a->num_cells_to_add; i++) { if (!eq_f1ap_cell_info(&a->cell_to_add[i].info, &b->cell_to_add[i].info)) return false; - if (a->cell_to_add[i].sys_info && b->cell_to_add[i].sys_info) { - if (!eq_f1ap_sys_info(a->cell_to_add[i].sys_info, b->cell_to_add[i].sys_info)) - return false; - } + if (!eq_f1ap_sys_info(a->cell_to_add[i].sys_info, b->cell_to_add[i].sys_info)) + return false; } /* to delete */ _F1_EQ_CHECK_INT(a->num_cells_to_delete, b->num_cells_to_delete); @@ -1251,12 +1249,13 @@ bool eq_f1ap_du_configuration_update(const f1ap_gnb_du_configuration_update_t *a /* to modify */ _F1_EQ_CHECK_INT(a->num_cells_to_modify, b->num_cells_to_modify); for (int i = 0; i < a->num_cells_to_modify; i++) { + if (!eq_f1ap_plmn(&a->cell_to_modify[i].old_plmn, &b->cell_to_modify[i].old_plmn)) + return false; + _F1_EQ_CHECK_LONG(a->cell_to_modify[i].old_nr_cellid, b->cell_to_modify[i].old_nr_cellid); if (!eq_f1ap_cell_info(&a->cell_to_modify[i].info, &b->cell_to_modify[i].info)) return false; - if (a->cell_to_modify[i].sys_info && b->cell_to_modify[i].sys_info) { - if (!eq_f1ap_sys_info(a->cell_to_modify[i].sys_info, b->cell_to_modify[i].sys_info)) - return false; - } + if (!eq_f1ap_sys_info(a->cell_to_modify[i].sys_info, b->cell_to_modify[i].sys_info)) + return false; } return true; } @@ -1276,6 +1275,10 @@ f1ap_gnb_du_configuration_update_t cp_f1ap_du_configuration_update(const f1ap_gn cp.transaction_id = msg->transaction_id; /* to add */ cp.num_cells_to_add = msg->num_cells_to_add; + for (int i = 0; i < cp.num_cells_to_add; ++i) { + cp.cell_to_add[i].info = copy_f1ap_served_cell_info(&msg->cell_to_add[i].info); + cp.cell_to_add[i].sys_info = copy_f1ap_gnb_du_system_info(msg->cell_to_add[i].sys_info); + } /* to delete */ cp.num_cells_to_delete = msg->num_cells_to_delete; for (int i = 0; i < cp.num_cells_to_delete; i++) { @@ -1285,31 +1288,10 @@ f1ap_gnb_du_configuration_update_t cp_f1ap_du_configuration_update(const f1ap_gn /* to modify */ cp.num_cells_to_modify = msg->num_cells_to_modify; for (int i = 0; i < cp.num_cells_to_modify; i++) { - cp.cell_to_modify[i].info = msg->cell_to_modify[i].info; - f1ap_served_cell_info_t *info = &cp.cell_to_modify[i].info; - if (info->measurement_timing_config_len > 0) { - info->measurement_timing_config = malloc_or_fail(info->measurement_timing_config_len * sizeof(*info->measurement_timing_config)); - for (int j = 0; j < info->measurement_timing_config_len; j++) - info->measurement_timing_config[j] = msg->cell_to_modify[i].info.measurement_timing_config[j]; - } - /* TAC */ - info->tac = calloc_or_fail(1, sizeof(*info->tac)); - *info->tac = *msg->cell_to_modify[i].info.tac; - /* System information */ - cp.cell_to_modify[i].sys_info = malloc_or_fail(sizeof(*cp.cell_to_modify[i].sys_info)); - f1ap_gnb_du_system_info_t *sys_info = cp.cell_to_modify[i].sys_info; - if (msg->cell_to_modify[i].sys_info->mib_length > 0) { - sys_info->mib_length = msg->cell_to_modify[i].sys_info->mib_length; - sys_info->mib = calloc_or_fail(msg->cell_to_modify[i].sys_info->mib_length, sizeof(*sys_info->mib)); - for (int j = 0; j < sys_info->mib_length; j++) - sys_info->mib[j] = msg->cell_to_modify[i].sys_info->mib[j]; - } - if (msg->cell_to_modify[i].sys_info->sib1_length > 0) { - sys_info->sib1_length = msg->cell_to_modify[i].sys_info->sib1_length; - sys_info->sib1 = calloc_or_fail(msg->cell_to_modify[i].sys_info->sib1_length, sizeof(*sys_info->sib1)); - for (int j = 0; j < sys_info->sib1_length; j++) - sys_info->sib1[j] = msg->cell_to_modify[i].sys_info->sib1[j]; - } + cp.cell_to_modify[i].old_plmn = msg->cell_to_modify[i].old_plmn; + cp.cell_to_modify[i].old_nr_cellid = msg->cell_to_modify[i].old_nr_cellid; + cp.cell_to_modify[i].info = copy_f1ap_served_cell_info(&msg->cell_to_modify[i].info); + cp.cell_to_modify[i].sys_info = copy_f1ap_gnb_du_system_info(msg->cell_to_modify[i].sys_info); } return cp; } diff --git a/openair2/F1AP/tests/f1ap_lib_test.c b/openair2/F1AP/tests/f1ap_lib_test.c index 4c75aa944e..80726d000d 100644 --- a/openair2/F1AP/tests/f1ap_lib_test.c +++ b/openair2/F1AP/tests/f1ap_lib_test.c @@ -425,6 +425,10 @@ static void test_f1ap_du_configuration_update(void) .transaction_id = 2, .num_cells_to_modify = 1, .cell_to_modify[0].info = info, + .cell_to_modify[0].old_nr_cellid = 1235UL, + .cell_to_modify[0].old_plmn.mcc = 208, + .cell_to_modify[0].old_plmn.mnc = 88, + .cell_to_modify[0].old_plmn.mnc_digit_length = 2, .num_cells_to_delete = 0, .cell_to_delete[0].nr_cellid = 1234UL, .cell_to_delete[0].plmn.mcc = 1, From 3e8636ce98abf1215ce2ad279634e4b6a55d0bf1 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Fri, 29 Nov 2024 11:20:18 +0100 Subject: [PATCH 18/32] Test gNB-DU config update: cell to add/delete --- openair2/F1AP/lib/f1ap_interface_management.c | 4 +--- openair2/F1AP/tests/f1ap_lib_test.c | 24 ++++++++++++++++++- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/openair2/F1AP/lib/f1ap_interface_management.c b/openair2/F1AP/lib/f1ap_interface_management.c index 777a4e33a9..182f40952d 100644 --- a/openair2/F1AP/lib/f1ap_interface_management.c +++ b/openair2/F1AP/lib/f1ap_interface_management.c @@ -1022,7 +1022,6 @@ F1AP_F1AP_PDU_t *encode_f1ap_du_configuration_update(const f1ap_gnb_du_configura /* mandatory */ /* c2. Served_Cells_To_Add */ if (msg->num_cells_to_add > 0) { - AssertFatal(false, "code for adding cells not tested\n"); asn1cSequenceAdd(out->protocolIEs.list, F1AP_GNBDUConfigurationUpdateIEs_t, ie2); ie2->id = F1AP_ProtocolIE_ID_id_Served_Cells_To_Add_List; ie2->criticality = F1AP_Criticality_reject; @@ -1080,7 +1079,6 @@ F1AP_F1AP_PDU_t *encode_f1ap_du_configuration_update(const f1ap_gnb_du_configura ie4->id = F1AP_ProtocolIE_ID_id_Served_Cells_To_Delete_List; ie4->criticality = F1AP_Criticality_reject; ie4->value.present = F1AP_GNBDUConfigurationUpdateIEs__value_PR_Served_Cells_To_Delete_List; - AssertFatal(msg->num_cells_to_delete == 0, "code for deleting cells not tested\n"); for (int i = 0; i < msg->num_cells_to_delete; i++) { asn1cSequenceAdd(ie4->value.choice.Served_Cells_To_Delete_List.list, F1AP_Served_Cells_To_Delete_ItemIEs_t, @@ -1134,8 +1132,8 @@ bool decode_f1ap_du_configuration_update(const F1AP_F1AP_PDU_t *pdu, f1ap_gnb_du } break; case F1AP_ProtocolIE_ID_id_Served_Cells_To_Add_List: { /* Served Cells To Add List */ - AssertError(out->num_cells_to_add > 0, return false, "at least 1 cell to add shall to be present"); out->num_cells_to_add = ie->value.choice.Served_Cells_To_Add_List.list.count; + AssertError(out->num_cells_to_add > 0, return false, "at least 1 cell to add shall to be present"); for (int i = 0; i < out->num_cells_to_add; i++) { F1AP_Served_Cells_To_Add_Item_t *served_cells_item = &((F1AP_Served_Cells_To_Add_ItemIEs_t *)ie->value.choice.Served_Cells_To_Add_List.list.array[i]) diff --git a/openair2/F1AP/tests/f1ap_lib_test.c b/openair2/F1AP/tests/f1ap_lib_test.c index 80726d000d..5d153f1aea 100644 --- a/openair2/F1AP/tests/f1ap_lib_test.c +++ b/openair2/F1AP/tests/f1ap_lib_test.c @@ -420,16 +420,38 @@ static void test_f1ap_du_configuration_update(void) .plmn.mnc_digit_length = 3, .tac = tac, }; + char *mtc2_data = "mtc2"; + uint8_t *mtc2 = (void*)strdup(mtc2_data); + int mtc2_len = strlen(mtc2_data); + f1ap_served_cell_info_t info2 = { + .mode = F1AP_MODE_FDD, + .fdd.ul_freqinfo.arfcn = 640000, + .fdd.ul_freqinfo.band = 78, + .fdd.dl_freqinfo.arfcn = 600000, + .fdd.dl_freqinfo.band = 78, + .fdd.ul_tbw.nrb = 66, + .fdd.ul_tbw.scs = 1, + .fdd.dl_tbw.nrb = 66, + .fdd.dl_tbw.scs = 1, + .measurement_timing_config_len = mtc2_len, + .measurement_timing_config = mtc2, + .nr_cellid = 123456, + .plmn.mcc = 2, + .plmn.mnc = 2, + .plmn.mnc_digit_length = 2, + }; /* create message */ f1ap_gnb_du_configuration_update_t orig = { .transaction_id = 2, + .num_cells_to_add = 1, + .cell_to_add[0].info = info2, .num_cells_to_modify = 1, .cell_to_modify[0].info = info, .cell_to_modify[0].old_nr_cellid = 1235UL, .cell_to_modify[0].old_plmn.mcc = 208, .cell_to_modify[0].old_plmn.mnc = 88, .cell_to_modify[0].old_plmn.mnc_digit_length = 2, - .num_cells_to_delete = 0, + .num_cells_to_delete = 1, .cell_to_delete[0].nr_cellid = 1234UL, .cell_to_delete[0].plmn.mcc = 1, .cell_to_delete[0].plmn.mnc = 1, From 9858a733c19983583a1221b327bc002d7261ba4d Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 28 Nov 2024 17:43:44 +0100 Subject: [PATCH 19/32] gnb_du_configuration_update_f1ap(): send to correct task --- openair2/LAYER2/NR_MAC_gNB/mac_rrc_ul_f1ap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 6d8f7a7115..99e6f02a57 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_rrc_ul_f1ap.c +++ b/openair2/LAYER2/NR_MAC_gNB/mac_rrc_ul_f1ap.c @@ -85,7 +85,7 @@ static void gnb_du_configuration_update_f1ap(const f1ap_gnb_du_configuration_upd /* free after copy */ free_f1ap_du_configuration_update(upd); /* send to RRC task */ - itti_send_msg_to_task(TASK_RRC_GNB, 0, msg); + itti_send_msg_to_task(TASK_DU_F1, 0, msg); } static void ue_context_setup_response_f1ap(const f1ap_ue_context_setup_t *req, const f1ap_ue_context_setup_t *resp) From 3e63f1dab2275dcc8073570a6a4e8767ffe995ac Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 28 Nov 2024 17:50:45 +0100 Subject: [PATCH 20/32] Correctly handle received gNB-DU config update ack --- openair2/F1AP/f1ap_du_interface_management.c | 9 +++------ openair2/GNB_APP/gnb_app.c | 4 ---- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/openair2/F1AP/f1ap_du_interface_management.c b/openair2/F1AP/f1ap_du_interface_management.c index 8f6114a9e5..f12b104263 100644 --- a/openair2/F1AP/f1ap_du_interface_management.c +++ b/openair2/F1AP/f1ap_du_interface_management.c @@ -262,12 +262,9 @@ int DU_handle_gNB_DU_CONFIGURATION_UPDATE_ACKNOWLEDGE(instance_t instance, free_f1ap_du_configuration_update_acknowledge(&in); return -1; } - // Allocate and send an ITTI message - MessageDef *msg_p = itti_alloc_new_message(TASK_DU_F1, 0, F1AP_GNB_DU_CONFIGURATION_UPDATE_ACKNOWLEDGE); - f1ap_gnb_du_configuration_update_acknowledge_t *msg = &F1AP_GNB_DU_CONFIGURATION_UPDATE_ACKNOWLEDGE(msg_p); - // Copy the decoded message to the ITTI message (RRC thread will free it) - *msg = in; // Copy the decoded message to the ITTI message (RRC thread will free it) - itti_send_msg_to_task(TASK_GNB_APP, GNB_MODULE_ID_TO_INSTANCE(assoc_id), msg_p); + + gnb_du_configuration_update_acknowledge(&in); + free_f1ap_du_configuration_update_acknowledge(&in); return 0; } diff --git a/openair2/GNB_APP/gnb_app.c b/openair2/GNB_APP/gnb_app.c index 354ed2e415..2162d9998e 100644 --- a/openair2/GNB_APP/gnb_app.c +++ b/openair2/GNB_APP/gnb_app.c @@ -238,10 +238,6 @@ void *gNB_app_task(void *args_p) break; - case F1AP_GNB_DU_CONFIGURATION_UPDATE_ACKNOWLEDGE: - LOG_E(GNB_APP, "[gNB %ld] Handling of %s message not implemented yet\n", instance, msg_name); - break; - case NGAP_DEREGISTERED_GNB_IND: LOG_W(GNB_APP, "[gNB %ld] Received %s: associated AMF %d\n", instance, msg_name, NGAP_DEREGISTERED_GNB_IND(msg_p).nb_amf); From 8e44b2d22ed460665a6cc8348d059ee02bb2face Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 27 Nov 2024 19:49:14 +0100 Subject: [PATCH 21/32] RRC: fix double free, already freed in scope of caller of this function --- openair2/RRC/NR/rrc_gNB_du.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/openair2/RRC/NR/rrc_gNB_du.c b/openair2/RRC/NR/rrc_gNB_du.c index fc2aec64b4..654ef04be1 100644 --- a/openair2/RRC/NR/rrc_gNB_du.c +++ b/openair2/RRC/NR/rrc_gNB_du.c @@ -533,8 +533,6 @@ void rrc_gNB_process_f1_du_configuration_update(f1ap_gnb_du_configuration_update /* Send DU Configuration Acknowledgement */ f1ap_gnb_du_configuration_update_acknowledge_t ack = {.transaction_id = conf_up->transaction_id}; rrc->mac_rrc.gnb_du_configuration_update_acknowledge(assoc_id, &ack); - /* free F1AP message after use */ - free_f1ap_du_configuration_update(conf_up); } void rrc_CU_process_f1_lost_connection(gNB_RRC_INST *rrc, f1ap_lost_connection_t *lc, sctp_assoc_t assoc_id) From 6b852a6fff4cedd7c83a61f2c9141d57e3039bf3 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 28 Nov 2024 17:42:46 +0100 Subject: [PATCH 22/32] CU_handle_gNB_DU_CONFIGURATION_UPDATE(): don't free the message of RRC The message is already freed at reception at RRC. --- openair2/F1AP/f1ap_cu_interface_management.c | 1 - 1 file changed, 1 deletion(-) diff --git a/openair2/F1AP/f1ap_cu_interface_management.c b/openair2/F1AP/f1ap_cu_interface_management.c index aed1ae6d23..bc5480a11b 100644 --- a/openair2/F1AP/f1ap_cu_interface_management.c +++ b/openair2/F1AP/f1ap_cu_interface_management.c @@ -143,7 +143,6 @@ int CU_handle_gNB_DU_CONFIGURATION_UPDATE(instance_t instance, sctp_assoc_t asso message_p->ittiMsgHeader.originInstance = assoc_id; f1ap_gnb_du_configuration_update_t *req = &F1AP_GNB_DU_CONFIGURATION_UPDATE(message_p); // RRC thread will free it *req = msg; // copy F1 message to ITTI - free_f1ap_du_configuration_update(&msg); LOG_D(F1AP, "Sending F1AP_GNB_DU_CONFIGURATION_UPDATE ITTI message \n"); itti_send_msg_to_task(TASK_RRC_GNB, GNB_MODULE_ID_TO_INSTANCE(instance), message_p); return 0; From 5f01cf6f93baec3b376a1477b09df33cced30d41 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 27 Nov 2024 16:49:04 +0100 Subject: [PATCH 23/32] Keep track of number of RUs in L1 Need to keep track of number of RUs, as the RU initialization increments the number of RUs. Therefore, subsequent initializations would fail if we don't decrement first. There is no better place (or I did not find it) to decrement the number of RUs than in nr_phy_free_RU(). For this reason, we also have to manually set the number of RUs in nr_prachsim to 1, otherwise the assert will trigger. --- openair1/PHY/INIT/nr_init_ru.c | 4 ++++ openair1/SIMULATION/NR_PHY/prachsim.c | 1 + 2 files changed, 5 insertions(+) diff --git a/openair1/PHY/INIT/nr_init_ru.c b/openair1/PHY/INIT/nr_init_ru.c index bf447645d6..674c70cdc3 100644 --- a/openair1/PHY/INIT/nr_init_ru.c +++ b/openair1/PHY/INIT/nr_init_ru.c @@ -180,4 +180,8 @@ void nr_phy_free_RU(RU_t *ru) free_and_zero(ru->common.beam_id[i]); free_and_zero(ru->common.beam_id); } + + PHY_VARS_gNB *gNB0 = ru->gNB_list[0]; + gNB0->num_RU--; + DevAssert(gNB0->num_RU >= 0); } diff --git a/openair1/SIMULATION/NR_PHY/prachsim.c b/openair1/SIMULATION/NR_PHY/prachsim.c index 08bf2e8fe6..de69fb6c16 100644 --- a/openair1/SIMULATION/NR_PHY/prachsim.c +++ b/openair1/SIMULATION/NR_PHY/prachsim.c @@ -467,6 +467,7 @@ int main(int argc, char **argv){ ru->nb_rx = n_rx; ru->num_gNB = 1; ru->gNB_list[0] = gNB; + gNB->num_RU = 1; gNB->gNB_config.carrier_config.num_tx_ant.value = 1; gNB->gNB_config.carrier_config.num_rx_ant.value = 1; if (mu == 0) From 0c24f1ff13e91e08b551f493263b4a0305f9177d Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 27 Nov 2024 16:50:57 +0100 Subject: [PATCH 24/32] Upon RRC exit: stop ongoing timer --- openair2/RRC/NR/rrc_gNB.c | 1 + 1 file changed, 1 insertion(+) diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index 9bf178afe2..9e552eaa2f 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -2498,6 +2498,7 @@ void *rrc_gnb_task(void *args_p) { switch (ITTI_MSG_ID(msg_p)) { case TERMINATE_MESSAGE: LOG_W(NR_RRC, " *** Exiting NR_RRC thread\n"); + timer_remove(stats_timer_id); itti_exit_task(); break; From 511b5f8ebdafc6d75f39d824d7379d5edda7f727 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 26 Nov 2024 07:45:37 +0100 Subject: [PATCH 25/32] Expose F1 primitives for next commits Need to proper forward declaration, as we otherwise need to make telnet_o1 module dependent on F1. Make functions publicly accessible, as the F1 functions are used in an upcoming commit to start the L1. --- openair2/F1AP/f1ap_du_rrc_message_transfer.h | 5 ++- openair2/GNB_APP/gnb_config.c | 43 +++++++++++--------- openair2/GNB_APP/gnb_config.h | 8 ++++ 3 files changed, 34 insertions(+), 22 deletions(-) diff --git a/openair2/F1AP/f1ap_du_rrc_message_transfer.h b/openair2/F1AP/f1ap_du_rrc_message_transfer.h index 4064a7cd2a..3c3bc228ad 100644 --- a/openair2/F1AP/f1ap_du_rrc_message_transfer.h +++ b/openair2/F1AP/f1ap_du_rrc_message_transfer.h @@ -34,9 +34,10 @@ #ifndef F1AP_DU_RRC_MESSAGE_TRANSFER_H_ #define F1AP_DU_RRC_MESSAGE_TRANSFER_H_ -#include "f1ap_common.h" +#include -int DU_handle_DL_RRC_MESSAGE_TRANSFER(instance_t instance, sctp_assoc_t assoc_id, uint32_t stream, F1AP_F1AP_PDU_t *pdu); +struct F1AP_F1AP_PDU; +int DU_handle_DL_RRC_MESSAGE_TRANSFER(instance_t instance, sctp_assoc_t assoc_id, uint32_t stream, struct F1AP_F1AP_PDU *pdu); int DU_send_UL_NR_RRC_MESSAGE_TRANSFER(sctp_assoc_t assoc_id, const f1ap_ul_rrc_message_t *msg); diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index 0612fa9677..7555d1f5d0 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -1166,7 +1166,7 @@ static int read_du_cell_info(configmodule_interface_t *cfg, return 1; } -static f1ap_tdd_info_t read_tdd_config(const NR_ServingCellConfigCommon_t *scc) +f1ap_tdd_info_t read_tdd_config(const NR_ServingCellConfigCommon_t *scc) { const NR_FrequencyInfoDL_t *dl = scc->downlinkConfigCommon->frequencyInfoDL; f1ap_tdd_info_t tdd = { @@ -1195,7 +1195,27 @@ static f1ap_fdd_info_t read_fdd_config(const NR_ServingCellConfigCommon_t *scc) return fdd; } -static f1ap_setup_req_t *RC_read_F1Setup(uint64_t id, +f1ap_gnb_du_system_info_t *get_sys_info(NR_BCCH_BCH_Message_t *mib, const NR_BCCH_DL_SCH_Message_t *sib1) +{ + int buf_len = 3; + f1ap_gnb_du_system_info_t *sys_info = calloc_or_fail(1, sizeof(*sys_info)); + + sys_info->mib = calloc_or_fail(buf_len, sizeof(*sys_info->mib)); + DevAssert(mib != NULL); + sys_info->mib_length = encode_MIB_NR(mib, 0, sys_info->mib, buf_len); + DevAssert(sys_info->mib_length == buf_len); + + DevAssert(sib1 != NULL); + NR_SIB1_t *bcch_SIB1 = sib1->message.choice.c1->choice.systemInformationBlockType1; + sys_info->sib1 = calloc_or_fail(NR_MAX_SIB_LENGTH / 8, sizeof(*sys_info->sib1)); + asn_enc_rval_t enc_rval = uper_encode_to_buffer(&asn_DEF_NR_SIB1, NULL, (void *)bcch_SIB1, sys_info->sib1, NR_MAX_SIB_LENGTH / 8); + AssertFatal(enc_rval.encoded > 0, "ASN1 message encoding failed (%s, %lu)!\n", enc_rval.failed_type->name, enc_rval.encoded); + sys_info->sib1_length = (enc_rval.encoded + 7) / 8; + + return sys_info; +} + +f1ap_setup_req_t *RC_read_F1Setup(uint64_t id, const char *name, const f1ap_served_cell_info_t *info, const NR_ServingCellConfigCommon_t *scc, @@ -1244,24 +1264,7 @@ static f1ap_setup_req_t *RC_read_F1Setup(uint64_t id, if (IS_SA_MODE(get_softmodem_params())) { // in NSA we don't transmit SIB1, so cannot fill DU system information // so cannot send MIB either - - int buf_len = 3; // this is what we assume in monolithic - req->cell[0].sys_info = calloc(1, sizeof(*req->cell[0].sys_info)); - AssertFatal(req->cell[0].sys_info != NULL, "out of memory\n"); - f1ap_gnb_du_system_info_t *sys_info = req->cell[0].sys_info; - sys_info->mib = calloc(buf_len, sizeof(*sys_info->mib)); - DevAssert(sys_info->mib != NULL); - DevAssert(mib != NULL); - // encode only the mib message itself - sys_info->mib_length = encode_MIB_NR_setup(mib->message.choice.mib, 0, sys_info->mib, buf_len); - DevAssert(sys_info->mib_length == buf_len); - - DevAssert(sib1 != NULL); - NR_SIB1_t *bcch_SIB1 = sib1->message.choice.c1->choice.systemInformationBlockType1; - sys_info->sib1 = calloc(NR_MAX_SIB_LENGTH / 8, sizeof(*sys_info->sib1)); - asn_enc_rval_t enc_rval = uper_encode_to_buffer(&asn_DEF_NR_SIB1, NULL, (void *)bcch_SIB1, sys_info->sib1, NR_MAX_SIB_LENGTH / 8); - AssertFatal(enc_rval.encoded > 0, "ASN1 message encoding failed (%s, %lu)!\n", enc_rval.failed_type->name, enc_rval.encoded); - sys_info->sib1_length = (enc_rval.encoded + 7) / 8; + req->cell[0].sys_info = get_sys_info(mib, sib1); } int num = read_version(TO_STRING(NR_RRC_VERSION), &req->rrc_ver[0], &req->rrc_ver[1], &req->rrc_ver[2]); diff --git a/openair2/GNB_APP/gnb_config.h b/openair2/GNB_APP/gnb_config.h index aaa945509c..baf4ad5d94 100644 --- a/openair2/GNB_APP/gnb_config.h +++ b/openair2/GNB_APP/gnb_config.h @@ -95,6 +95,14 @@ gNB_RRC_INST *RCconfig_NRRRC(); int RCconfig_NR_NG(MessageDef *msg_p, uint32_t i); int RCconfig_NR_X2(MessageDef *msg_p, uint32_t i); void wait_f1_setup_response(void); +f1ap_setup_req_t *RC_read_F1Setup(uint64_t id, + const char *name, + const f1ap_served_cell_info_t *info, + const NR_ServingCellConfigCommon_t *scc, + NR_BCCH_BCH_Message_t *mib, + const NR_BCCH_DL_SCH_Message_t *sib1); +f1ap_tdd_info_t read_tdd_config(const NR_ServingCellConfigCommon_t *scc); +f1ap_gnb_du_system_info_t *get_sys_info(NR_BCCH_BCH_Message_t *mib, const NR_BCCH_DL_SCH_Message_t *sib1); int gNB_app_handle_f1ap_gnb_cu_configuration_update(f1ap_gnb_cu_configuration_update_t *gnb_cu_cfg_update); MessageDef *RCconfig_NR_CU_E1(const E1_t *entity); ngran_node_t get_node_type(void); From 8e32f566fca66413d94df2febdb7ceb9cd80e4bb Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Fri, 29 Nov 2024 11:22:42 +0100 Subject: [PATCH 26/32] Refactor code to stop L1 Refactor L1 stopping code into a function. The function is basically the same as what we were doing at the end of main() of nr-softmodem.c. This will be reused to support the "soft-restart" functionality in the next commits. --- executables/nr-softmodem.c | 74 +++++++++++++++++++++++++------------- 1 file changed, 50 insertions(+), 24 deletions(-) diff --git a/executables/nr-softmodem.c b/executables/nr-softmodem.c index 8de047ea06..de06a34d8f 100644 --- a/executables/nr-softmodem.c +++ b/executables/nr-softmodem.c @@ -401,6 +401,54 @@ void terminate_task(task_id_t task_id, module_id_t mod_id) { //extern void free_transport(PHY_VARS_gNB *); extern void nr_phy_free_RU(RU_t *); +int stop_L1(module_id_t gnb_id) +{ + RU_t *ru = RC.ru[gnb_id]; + if (!ru) { + LOG_W(GNB_APP, "no RU configured\n"); + return -1; + } + + if (!RC.gNB[gnb_id]->configured) { + LOG_W(GNB_APP, "L1 already stopped\n"); + return -1; + } + + LOG_I(GNB_APP, "stopping nr-softmodem\n"); + oai_exit = 1; + + /* these tasks/layers need to pick up new configuration */ + if (RC.nb_nr_L1_inst > 0) + stop_gNB(RC.nb_nr_L1_inst); + + if (RC.nb_RU > 0) + stop_RU(RC.nb_RU); + + /* stop trx devices, multiple carrier currently not supported by RU */ + if (ru->rfdevice.trx_stop_func) { + ru->rfdevice.trx_stop_func(&ru->rfdevice); + LOG_I(GNB_APP, "turned off RU rfdevice\n"); + } + + if (ru->ifdevice.trx_stop_func) { + ru->ifdevice.trx_stop_func(&ru->ifdevice); + LOG_I(GNB_APP, "turned off RU ifdevice\n"); + } + + /* release memory used by the RU/gNB threads (incomplete), after all + * threads have been stopped (they partially use the same memory) */ + for (int inst = 0; inst < RC.nb_RU; inst++) { + nr_phy_free_RU(RC.ru[inst]); + } + + for (int inst = 0; inst < RC.nb_nr_L1_inst; inst++) { + phy_free_nr_gNB(RC.gNB[inst]); + } + + RC.gNB[gnb_id]->configured = 0; + return 0; +} + static void wait_nfapi_init(char *thread_name) { pthread_mutex_lock( &nfapi_sync_mutex ); @@ -629,37 +677,15 @@ int main( int argc, char **argv ) { printf("TYPE TO TERMINATE\n"); itti_wait_tasks_end(NULL); printf("Returned from ITTI signal handler\n"); - oai_exit=1; - // cleanup - if (RC.nb_nr_L1_inst > 0) - stop_gNB(RC.nb_nr_L1_inst); - - if (RC.nb_RU > 0) - stop_RU(RC.nb_RU); - - /* release memory used by the RU/gNB threads (incomplete), after all - * threads have been stopped (they partially use the same memory) */ - for (int inst = 0; inst < RC.nb_RU; inst++) { - nr_phy_free_RU(RC.ru[inst]); - } - - for (int inst = 0; inst < RC.nb_nr_L1_inst; inst++) { - phy_free_nr_gNB(RC.gNB[inst]); - } + if (RC.nb_nr_L1_inst > 0 || RC.nb_RU > 0) + stop_L1(0); pthread_cond_destroy(&sync_cond); pthread_mutex_destroy(&sync_mutex); pthread_cond_destroy(&nfapi_sync_cond); pthread_mutex_destroy(&nfapi_sync_mutex); - // *** Handle per CC_id openair0 - - for(ru_id = 0; ru_id < RC.nb_RU; ru_id++) { - if (RC.ru[ru_id]->ifdevice.trx_end_func) - RC.ru[ru_id]->ifdevice.trx_end_func(&RC.ru[ru_id]->ifdevice); - } - free(pckg); logClean(); printf("Bye.\n"); From 68ae3ea11c4d6ce2ed0732f37bd5ffbcdd0cb933 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Fri, 29 Nov 2024 11:25:55 +0100 Subject: [PATCH 27/32] Implement start_L1L2() Implements the re-start of the L1 after it has been stopped through stop_L1(). It takes into account some changes, such as bandwidth, or frequency, when restarting the L1. To this end, it also triggers a gNB-DU configuration update message on each restart to inform the CU about any changes in the DU. FDD has not been tested. --- executables/nr-softmodem.c | 51 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/executables/nr-softmodem.c b/executables/nr-softmodem.c index de06a34d8f..96527fb303 100644 --- a/executables/nr-softmodem.c +++ b/executables/nr-softmodem.c @@ -449,6 +449,57 @@ int stop_L1(module_id_t gnb_id) return 0; } +/* + * Restart the nr-softmodem after it has been soft-stopped with stop_L1L2() + */ +#include "openair2/LAYER2/NR_MAC_gNB/mac_proto.h" +int start_L1L2(module_id_t gnb_id) +{ + LOG_I(GNB_APP, "starting nr-softmodem\n"); + /* block threads */ + oai_exit = 0; + sync_var = -1; + extern void init_sched_response(void); + init_sched_response(); + + /* update config */ + gNB_MAC_INST *mac = RC.nrmac[0]; + NR_ServingCellConfigCommon_t *scc = mac->common_channels[0].ServingCellConfigCommon; + nr_mac_config_scc(mac, scc, &mac->radio_config); + + NR_BCCH_BCH_Message_t *mib = mac->common_channels[0].mib; + const NR_BCCH_DL_SCH_Message_t *sib1 = mac->common_channels[0].sib1; + + /* update existing config in F1 Setup request structures */ + f1ap_setup_req_t *sr = mac->f1_config.setup_req; + DevAssert(sr->num_cells_available == 1); + f1ap_served_cell_info_t *info = &sr->cell[0].info; + DevAssert(info->mode == F1AP_MODE_TDD); + DevAssert(scc->tdd_UL_DL_ConfigurationCommon != NULL); + info->tdd = read_tdd_config(scc); /* updates radio config */ + /* send gNB-DU configuration update to RRC */ + f1ap_gnb_du_configuration_update_t update = { + .transaction_id = 1, + .num_cells_to_modify = 1, + }; + update.cell_to_modify[0].old_nr_cellid = info->nr_cellid; + update.cell_to_modify[0].info = *info; + update.cell_to_modify[0].sys_info = get_sys_info(mib, sib1); + mac->mac_rrc.gnb_du_configuration_update(&update); + + init_NR_RU(config_get_if(), NULL); + + start_NR_RU(); + wait_RUs(); + init_eNB_afterRU(); + LOG_I(GNB_APP, "Sending sync to all threads\n"); + pthread_mutex_lock(&sync_mutex); + sync_var=0; + pthread_cond_broadcast(&sync_cond); + pthread_mutex_unlock(&sync_mutex); + return 0; +} + static void wait_nfapi_init(char *thread_name) { pthread_mutex_lock( &nfapi_sync_mutex ); From aba506e5e3669a10e3e4e99a6fdf133ead1b5360 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 5 Oct 2023 08:53:12 +0200 Subject: [PATCH 28/32] Add basic O1 telnet module This adds a basic telnet module to support the O1 interface at the DU, to be used together with the oai/o1-adapter>. Concretely, this first version supports to - read/modify cell parameters - stop/start the L1 --- common/utils/telnetsrv/CMakeLists.txt | 8 +- common/utils/telnetsrv/DOC/telneto1.md | 176 +++++++++++ common/utils/telnetsrv/DOC/telnetsrv.md | 1 + common/utils/telnetsrv/telnetsrv_o1.c | 391 ++++++++++++++++++++++++ 4 files changed, 575 insertions(+), 1 deletion(-) create mode 100644 common/utils/telnetsrv/DOC/telneto1.md create mode 100644 common/utils/telnetsrv/telnetsrv_o1.c diff --git a/common/utils/telnetsrv/CMakeLists.txt b/common/utils/telnetsrv/CMakeLists.txt index a7da081f09..5f190edc02 100644 --- a/common/utils/telnetsrv/CMakeLists.txt +++ b/common/utils/telnetsrv/CMakeLists.txt @@ -75,7 +75,13 @@ add_library(telnetsrv_rrc MODULE telnetsrv_rrc.c) target_link_libraries(telnetsrv_rrc PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs) add_dependencies(telnetsrv telnetsrv_rrc) +message(STATUS "Add CI specific telnet functions in libtelnetsrv_o1.so") +add_library(telnetsrv_o1 MODULE telnetsrv_o1.c) +target_link_libraries(telnetsrv_o1 PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs) +add_dependencies(telnetsrv telnetsrv_o1) + # all libraries should be written to root build dir -set_target_properties(telnetsrv telnetsrv_enb telnetsrv_5Gue telnetsrv_ci telnetsrv_ciUE telnetsrv_bearer telnetsrv_rrc +set_target_properties(telnetsrv telnetsrv_enb telnetsrv_5Gue telnetsrv_ci telnetsrv_ciUE + telnetsrv_bearer telnetsrv_rrc telnetsrv_o1 PROPERTIES LIBRARY_OUTPUT_DIRECTORY ../../.. ) diff --git a/common/utils/telnetsrv/DOC/telneto1.md b/common/utils/telnetsrv/DOC/telneto1.md new file mode 100644 index 0000000000..4c6b9a3b4d --- /dev/null +++ b/common/utils/telnetsrv/DOC/telneto1.md @@ -0,0 +1,176 @@ +[[_TOC_]] + +The telnet O1 module (`telnetsrv_o1.c`) can be used to perform some O1-related +actions (reading data, starting and stopping the nr-softmodem, reconfigurating +frequency and bandwidth). + +# General usage + +The usage is similar to the general telnet usage, but in short: +``` +./build_oai --ninja -c --gNB --nrUE --build-lib telnetsrv +``` +to build everything including the telnet library. Then, run the nr-softmodem +by activating telnet and loading the `o1` module: +``` +./nr-softmodem -O --telnetsrv --telnetsrv.shrmod o1 +``` + +Afterwards, it should be possible to connect via telnet on localhost, port +9090. Use `help` to get help on the different command sections, and type e.g. +`o1 stats` to get statistics (more information further below): + +``` +$ telnet 127.0.0.1 9090 +Trying 127.0.0.1... +Connected to 127.0.0.1. +Escape character is '^]'. + +softmodem_gnb> help +[...] + module 4 = o1: + o1 stats + o1 config ? + o1 stop_modem + o1 start_modem +[...] +softmodem_gnb> o1 stats +[...] +softmodem_gnb> exit +Connection closed by foreign host. +``` + +It also possible to send a command "directly from the command line", by piping +the command into netcat: +``` +echo o1 stats | nc -N 127.0.0.1 9090 +``` + +Note that only one telnet client can be connected at a time. + +# Get statistics + +Use the `o1 stats` command. The output is in JSON format: +```json +{ + "o1-config": { + "BWP": { + "dl": [ + { + "bwp3gpp:isInitialBwp": true, + "bwp3gpp:numberOfRBs": 106, + "bwp3gpp:startRB": 0, + "bwp3gpp:subCarrierSpacing": 30 + } + ], + "ul": [ + { + "bwp3gpp:isInitialBwp": true, + "bwp3gpp:numberOfRBs": 106, + "bwp3gpp:startRB": 0, + "bwp3gpp:subCarrierSpacing": 30 + } + ] + }, + "NRCELLDU": { + "nrcelldu3gpp:ssbFrequency": 641280, + "nrcelldu3gpp:arfcnDL": 640008, + "nrcelldu3gpp:bSChannelBwDL": 40, + "nrcelldu3gpp:arfcnUL": 640008, + "nrcelldu3gpp:bSChannelBwUL": 40, + "nrcelldu3gpp:nRPCI": 0, + "nrcelldu3gpp:nRTAC": 1, + "nrcelldu3gpp:mcc": "208", + "nrcelldu3gpp:mnc": "95", + "nrcelldu3gpp:sd": 16777215, + "nrcelldu3gpp:sst": 1 + }, + "device": { + "gnbId": 1, + "gnbName": "gNB-Eurecom-5GNRBox", + "vendor": "OpenAirInterface" + } + }, + "O1-Operational": { + "frame-type": "tdd", + "band-number": 78, + "num-ues": 1, + "ues": [ + 6876 + ], + "load": 9, + "ues-thp": [ + { + "rnti": 6876, + "dl": 3279, + "ul": 2725 + } + ] + } +} +``` + +Note that no actual JSON engine is used, so no actual verification is done; it +is for convenience of the consumer. To verify, you can employ `jq`: +``` +echo o1 stats | nc -N 127.0.0.1 9090 | awk '/^{$/, /^}$/' | jq . +``` +(`awk`'s pattern matching makes that only everything between the first `{` and +its corresponding `}` is printed). + +There are two sections: +1. `.o1-config` show some stats that map directly to the O1 Netconf model. Note + that only one MCC/MNC/SD/SST (each) are supported right now. Also, note that + as per 3GPP specifications, SD of value `0xffffff` (16777215 in decimal) + means "no SD". `bSChannelBwDL/UL` is reported in MHz. +2. `.O1-operational` output some statistics that do not map yet to any netconf + parameters, but that might be useful nevertheless for a consumer. + +# Write a new configuration + +Use `o1 config` to write a configuration: +``` +echo o1 config nrcelldu3gpp:ssbFrequency 620736 nrcelldu3gpp:arfcnDL 620020 nrcelldu3gpp:bSChannelBwDL 51 bwp3gpp:numberOfRBs 51 bwp3gpp:startRB 0 | nc -N 127.0.0.1 9090 +``` +You have to pass the above parameters in exactly this order. The softmodem +needs to be stopped; it will pick up the new configuration when starting the +softmodem again. + +Note that you cannot switch three-quarter sampling for this as of now. + +For values of the configuration, refer to the next section. + +# Use hardcoded configuration + +Use `o1 bwconfig` to write a hard-coded configuration for 20 or 40 MHz cells: +``` +echo o1 bwconfig 20 | nc -N 127.0.0.1 9090 +echo o1 bwconfig 40 | nc -N 127.0.0.1 9090 +``` + +The softmodem needs to be stopped; it will pick up the new configuration when +starting the softmodem again. + +Use `o1 stats` to see which configurations are set by these commands for the +parameters `nrcelldu3gpp:ssbFrequency`, `nrcelldu3gpp:arfcnDL`, +`nrcelldu3gpp:arfcnUL`, `nrcelldu3gpp:bSChannelBwDL`, +`nrcelldu3gpp:bSChannelBwUL`, and `bwp3gpp:numberOfRBsbwp3gpp:startRB`. +Furthermore, for 20MHz, it *disables* three-quarter sampling, whereas it +*enables* three-quarter sampling for 40MHz. + +# Restart the softmodem + +Use `o1 stop_modem` to stop the `nr-softmodem`. To restart the softmodem, use +`o1 start_modem`: +``` +echo o1 stop_modem | nc -N 127.0.0.1 9090 +echo o1 start_modem | nc -N 127.0.0.1 9090 +``` + +In fact, stopping terminates all L1 threads. It will be as if the softmodem +"freezes", and no periodical output of statistics will occur (the O1 telnet +interface will still work, though). Starting again will "defreeze" the +softmodem. + +Upon restart, the DU sends a gNB-DU configuration update to the CU to inform it +about the updated configuration. Therefore, this also works in F1. diff --git a/common/utils/telnetsrv/DOC/telnetsrv.md b/common/utils/telnetsrv/DOC/telnetsrv.md index 11c6cf071f..68051e7df8 100644 --- a/common/utils/telnetsrv/DOC/telnetsrv.md +++ b/common/utils/telnetsrv/DOC/telnetsrv.md @@ -3,5 +3,6 @@ The oai embedded telnet server is an optional monitoring and debugging tool. It * [Using the telnet server](telnetusage.md) * [Adding commands to the oai telnet server](telnetaddcmd.md) * [telnet server architecture ](telnetarch.md) +* [on the telnet O1 module](telneto1.md) [oai Wikis home](https://gitlab.eurecom.fr/oai/openairinterface5g/wikis/home) diff --git a/common/utils/telnetsrv/telnetsrv_o1.c b/common/utils/telnetsrv/telnetsrv_o1.c new file mode 100644 index 0000000000..36cffdf18d --- /dev/null +++ b/common/utils/telnetsrv/telnetsrv_o1.c @@ -0,0 +1,391 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include + +#define TELNETSERVERCODE +#include "telnetsrv.h" + +#include "openair2/RRC/NR/nr_rrc_defs.h" +#include "openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h" +#include "openair2/RRC/NR/nr_rrc_config.h" +#include "openair2/LAYER2/NR_MAC_gNB/mac_proto.h" +#include "openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c" +#include "common/utils/nr/nr_common.h" + +#define ERROR_MSG_RET(mSG, aRGS...) do { prnt("FAILURE: " mSG, ##aRGS); return 1; } while (0) + +#define ISINITBWP "bwp3gpp:isInitialBwp" +//#define CYCLPREF "bwp3gpp:cyclicPrefix" +#define NUMRBS "bwp3gpp:numberOfRBs" +#define STARTRB "bwp3gpp:startRB" +#define BWPSCS "bwp3gpp:subCarrierSpacing" + +#define SSBFREQ "nrcelldu3gpp:ssbFrequency" +#define ARFCNDL "nrcelldu3gpp:arfcnDL" +#define BWDL "nrcelldu3gpp:bSChannelBwDL" +#define ARFCNUL "nrcelldu3gpp:arfcnUL" +#define BWUL "nrcelldu3gpp:bSChannelBwUL" +#define PCI "nrcelldu3gpp:nRPCI" +#define TAC "nrcelldu3gpp:nRTAC" +#define MCC "nrcelldu3gpp:mcc" +#define MNC "nrcelldu3gpp:mnc" +#define SD "nrcelldu3gpp:sd" +#define SST "nrcelldu3gpp:sst" + +typedef struct b { + long int dl; + long int ul; +} b_t; + +typedef struct ue_stat { + rnti_t rnti; + b_t thr; +} ue_stat_t; + +#define PRINTLIST_i(len, fmt, ...) \ + { \ + for (int i = 0; i < len; ++i) { \ + if (i != 0) prnt(", "); \ + prnt(fmt, __VA_ARGS__); \ + } \ + } \ + +static int get_stats(char *buf, int debug, telnet_printfunc_t prnt) +{ + if (buf) + ERROR_MSG_RET("no parameter allowed\n"); + + gNB_MAC_INST *mac = RC.nrmac[0]; + AssertFatal(mac != NULL, "need MAC\n"); + NR_SCHED_LOCK(&mac->sched_lock); + + const f1ap_setup_req_t *sr = mac->f1_config.setup_req; + const f1ap_served_cell_info_t *cell_info = &sr->cell[0].info; + + const NR_ServingCellConfigCommon_t *scc = mac->common_channels[0].ServingCellConfigCommon; + const NR_FrequencyInfoDL_t *frequencyInfoDL = scc->downlinkConfigCommon->frequencyInfoDL; + const NR_FrequencyInfoUL_t *frequencyInfoUL = scc->uplinkConfigCommon->frequencyInfoUL; + frame_type_t frame_type = get_frame_type(*frequencyInfoDL->frequencyBandList.list.array[0], *scc->ssbSubcarrierSpacing); + const NR_BWP_t *initialDL = &scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters; + const NR_BWP_t *initialUL = &scc->uplinkConfigCommon->initialUplinkBWP->genericParameters; + + int scs = initialDL->subcarrierSpacing; + AssertFatal(scs == initialUL->subcarrierSpacing, "different SCS for UL/DL not supported!\n"); + int band = *frequencyInfoDL->frequencyBandList.list.array[0]; + int nrb = frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth; + AssertFatal(nrb == frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth, "different BW for UL/DL not supported!\n"); + frequency_range_t fr = band > 256 ? FR2 : FR1; + int bw_index = get_supported_band_index(scs, fr, nrb); + int bw_mhz = get_supported_bw_mhz(fr, bw_index); + + const mac_stats_t *stat = &mac->mac_stats; + static mac_stats_t last = {0}; + int diff_used = stat->used_prb_aggregate - last.used_prb_aggregate; + int diff_total = stat->total_prb_aggregate - last.total_prb_aggregate; + int load = diff_total > 0 ? 100 * diff_used / diff_total : 0; + last = *stat; + + static struct timespec tp_last = {0}; + struct timespec tp_now; + clock_gettime(CLOCK_MONOTONIC, &tp_now); + size_t diff_msec = (tp_now.tv_sec - tp_last.tv_sec) * 1000 + (tp_now.tv_nsec - tp_last.tv_nsec) / 1000000; + tp_last = tp_now; + + const int srb_flag = 0; + const int rb_id = 1; + static b_t last_total[MAX_MOBILES_PER_GNB] = {0}; // TODO: hash table? + ue_stat_t ue_stat[MAX_MOBILES_PER_GNB] = {0}; + int num_ues = 0; + UE_iterator((NR_UE_info_t **)mac->UE_info.list, it) { + nr_rlc_statistics_t rlc = {0}; + nr_rlc_get_statistics(it->rnti, srb_flag, rb_id, &rlc); + b_t *lt = &last_total[num_ues]; + ue_stat_t *ue_s = &ue_stat[num_ues]; + ue_s->rnti = it->rnti; + // static var last_total: we might have old data, larger than what + // reports RLC, leading to a huge number -> cut off to zero + if (lt->dl > rlc.txpdu_bytes) + lt->dl = rlc.txpdu_bytes; + if (lt->ul > rlc.rxpdu_bytes) + lt->ul = rlc.rxpdu_bytes; + ue_s->thr.dl = (rlc.txpdu_bytes - lt->dl) * 8 / diff_msec; + ue_s->thr.ul = (rlc.rxpdu_bytes - lt->ul) * 8 / diff_msec; + lt->dl = rlc.txpdu_bytes; + lt->ul = rlc.rxpdu_bytes; + num_ues++; + } + + prnt("{\n"); + prnt(" \"o1-config\": {\n"); + + prnt(" \"BWP\": {\n"); + prnt(" \"dl\": [{\n"); + prnt(" \"" ISINITBWP "\": true,\n"); + //prnt(" \"" CYCLPREF "\": %ld,\n", *initialDL->cyclicPrefix); + prnt(" \"" NUMRBS "\": %ld,\n", NRRIV2BW(initialDL->locationAndBandwidth, MAX_BWP_SIZE)); + prnt(" \"" STARTRB "\": %ld,\n", NRRIV2PRBOFFSET(initialDL->locationAndBandwidth, MAX_BWP_SIZE)); + prnt(" \"" BWPSCS "\": %ld\n", 15 * (1U << scs)); + prnt(" }],\n"); + prnt(" \"ul\": [{\n"); + prnt(" \"" ISINITBWP "\": true,\n"); + //prnt(" \"" CYCLPREF "\": %ld,\n", *initialUL->cyclicPrefix); + prnt(" \"" NUMRBS "\": %ld,\n", NRRIV2BW(initialUL->locationAndBandwidth, MAX_BWP_SIZE)); + prnt(" \"" STARTRB "\": %ld,\n", NRRIV2PRBOFFSET(initialUL->locationAndBandwidth, MAX_BWP_SIZE)); + prnt(" \"" BWPSCS "\": %ld\n", 15 * (1U << scs)); + prnt(" }]\n"); + prnt(" },\n"); + + prnt(" \"NRCELLDU\": {\n"); + prnt(" \"" SSBFREQ "\": %ld,\n", *scc->downlinkConfigCommon->frequencyInfoDL->absoluteFrequencySSB); + prnt(" \"" ARFCNDL "\": %ld,\n", frequencyInfoDL->absoluteFrequencyPointA); + prnt(" \"" BWDL "\": %ld,\n", bw_mhz); + prnt(" \"" ARFCNUL "\": %ld,\n", frequencyInfoUL->absoluteFrequencyPointA ? *frequencyInfoUL->absoluteFrequencyPointA : frequencyInfoDL->absoluteFrequencyPointA); + prnt(" \"" BWUL "\": %ld,\n", bw_mhz); + prnt(" \"" PCI "\": %ld,\n", *scc->physCellId); + prnt(" \"" TAC "\": %ld,\n", *cell_info->tac); + prnt(" \"" MCC "\": \"%03d\",\n", cell_info->plmn.mcc); + prnt(" \"" MNC "\": \"%0*d\",\n", cell_info->plmn.mnc_digit_length, cell_info->plmn.mnc); + prnt(" \"" SD "\": %d,\n", cell_info->nssai[0].sd); + prnt(" \"" SST "\": %d\n", cell_info->nssai[0].sst); + prnt(" },\n"); + prnt(" \"device\": {\n"); + prnt(" \"gnbId\": %d,\n", sr->gNB_DU_id); + prnt(" \"gnbName\": \"%s\",\n", sr->gNB_DU_name); + prnt(" \"vendor\": \"OpenAirInterface\"\n"); + prnt(" }\n"); + prnt(" },\n"); + + prnt(" \"O1-Operational\": {\n"); + prnt(" \"frame-type\": \"%s\",\n", frame_type == TDD ? "tdd" : "fdd"); + prnt(" \"band-number\": %ld,\n", band); + prnt(" \"num-ues\": %d,\n", num_ues); + prnt(" \"ues\": ["); PRINTLIST_i(num_ues, "%d", ue_stat[i].rnti); prnt("],\n"); + prnt(" \"load\": %d,\n", load); + prnt(" \"ues-thp\": ["); + PRINTLIST_i(num_ues, "\n {\"rnti\": %d, \"dl\": %ld, \"ul\": %ld}", ue_stat[i].rnti, ue_stat[i].thr.dl, ue_stat[i].thr.ul); + prnt("\n ]\n"); + prnt(" }\n"); + prnt("}\n"); + prnt("OK\n"); + NR_SCHED_UNLOCK(&mac->sched_lock); + return 0; +} + +static int read_long(const char *buf, const char *end, const char *id, long *val) +{ + const char *curr = buf; + while (isspace(*curr) && curr < end) // skip leading spaces + curr++; + int len = strlen(id); + if (curr + len >= end) + return -1; + if (strncmp(curr, id, len) != 0) // check buf has id + return -1; + curr += len; + while (isspace(*curr) && curr < end) // skip middle spaces + curr++; + if (curr >= end) + return -1; + int nread = sscanf(curr, "%ld", val); + if (nread != 1) + return -1; + while (isdigit(*curr) && curr < end) // skip all digits read above + curr++; + if (curr > end) + return -1; + return curr - buf; +} + +bool running = true; // in the beginning, the softmodem is started automatically +static int set_config(char *buf, int debug, telnet_printfunc_t prnt) +{ + if (!buf) + ERROR_MSG_RET("need param: o1 config param1 val1 [param2 val2 ...]\n"); + if (running) + ERROR_MSG_RET("cannot set parameters while L1 is running\n"); + const char *end = buf + strlen(buf); + + /* we need to update the following fields to change frequency and/or + * bandwidth: + * --gNBs.[0].servingCellConfigCommon.[0].absoluteFrequencySSB 620736 -> SSBFREQ + * --gNBs.[0].servingCellConfigCommon.[0].dl_absoluteFrequencyPointA 620020 -> ARFCNDL + * --gNBs.[0].servingCellConfigCommon.[0].dl_carrierBandwidth 51 -> BWDL + * --gNBs.[0].servingCellConfigCommon.[0].initialDLBWPlocationAndBandwidth 13750 -> NUMRBS + STARTRB + * --gNBs.[0].servingCellConfigCommon.[0].ul_carrierBandwidth 51 -> BWUL? + * --gNBs.[0].servingCellConfigCommon.[0].initialULBWPlocationAndBandwidth 13750 -> ? + */ + + int processed = 0; + int pos = 0; + + long ssbfreq; + processed = read_long(buf + pos, end, SSBFREQ, &ssbfreq); + if (processed < 0) + ERROR_MSG_RET("could not read " SSBFREQ " at index %d\n", pos + processed); + pos += processed; + prnt("setting " SSBFREQ ": %ld [len %d]\n", ssbfreq, pos); + + long arfcn; + processed = read_long(buf + pos, end, ARFCNDL, &arfcn); + if (processed < 0) + ERROR_MSG_RET("could not read " ARFCNDL " at index %d\n", pos + processed); + pos += processed; + prnt("setting " ARFCNDL ": %ld [len %d]\n", arfcn, pos); + + long bwdl; + processed = read_long(buf + pos, end, BWDL, &bwdl); + if (processed < 0) + ERROR_MSG_RET("could not read " BWDL " at index %d\n", pos + processed); + pos += processed; + prnt("setting " BWDL ": %ld [len %d]\n", bwdl, pos); + + long numrbs; + processed = read_long(buf + pos, end, NUMRBS, &numrbs); + if (processed < 0) + ERROR_MSG_RET("could not read " NUMRBS " at index %d\n", pos + processed); + pos += processed; + prnt("setting " NUMRBS ": %ld [len %d]\n", numrbs, pos); + + long startrb; + processed = read_long(buf + pos, end, STARTRB, &startrb); + if (processed < 0) + ERROR_MSG_RET("could not read " STARTRB " at index %d\n", pos + processed); + pos += processed; + prnt("setting " STARTRB ": %ld [len %d]\n", startrb, pos); + + int locationAndBandwidth = PRBalloc_to_locationandbandwidth0(numrbs, startrb, MAX_BWP_SIZE); + prnt("inferred locationAndBandwidth: %d\n", locationAndBandwidth); + prnt("OK\n"); + return 0; +} + +static int set_bwconfig(char *buf, int debug, telnet_printfunc_t prnt) +{ + if (running) + ERROR_MSG_RET("cannot set parameters while L1 is running\n"); + if (!buf) + ERROR_MSG_RET("need param: o1 bwconfig \n"); + + char *end = NULL; + if (NULL != (end = strchr(buf, '\n'))) + *end = 0; + if (NULL != (end = strchr(buf, '\r'))) + *end = 0; + + gNB_MAC_INST *mac = RC.nrmac[0]; + NR_ServingCellConfigCommon_t *scc = mac->common_channels[0].ServingCellConfigCommon; + NR_FrequencyInfoDL_t *frequencyInfoDL = scc->downlinkConfigCommon->frequencyInfoDL; + NR_BWP_t *initialDL = &scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters; + NR_FrequencyInfoUL_t *frequencyInfoUL = scc->uplinkConfigCommon->frequencyInfoUL; + NR_BWP_t *initialUL = &scc->uplinkConfigCommon->initialUplinkBWP->genericParameters; + if (strcmp(buf, "40") == 0) { + *scc->downlinkConfigCommon->frequencyInfoDL->absoluteFrequencySSB = 641280; + frequencyInfoDL->absoluteFrequencyPointA = 640008; + AssertFatal(frequencyInfoUL->absoluteFrequencyPointA == NULL, "only handle TDD\n"); + frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth = 106; + initialDL->locationAndBandwidth = 28875; + frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth = 106; + initialUL->locationAndBandwidth = 28875; + get_softmodem_params()->threequarter_fs = 1; + } else if (strcmp(buf, "20") == 0) { + *scc->downlinkConfigCommon->frequencyInfoDL->absoluteFrequencySSB = 641280; + frequencyInfoDL->absoluteFrequencyPointA = 640596; + AssertFatal(frequencyInfoUL->absoluteFrequencyPointA == NULL, "only handle TDD\n"); + frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth = 51; + initialDL->locationAndBandwidth = 13750; + frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth = 51; + initialUL->locationAndBandwidth = 13750; + get_softmodem_params()->threequarter_fs = 0; + } else { + ERROR_MSG_RET("unhandled option %s\n", buf); + } + + free(RC.nrmac[0]->sched_ctrlCommon); + RC.nrmac[0]->sched_ctrlCommon = NULL; + + free_MIB_NR(mac->common_channels[0].mib); + mac->common_channels[0].mib = get_new_MIB_NR(scc); + + const f1ap_served_cell_info_t *info = &mac->f1_config.setup_req->cell[0].info; + nr_mac_configure_sib1(mac, &info->plmn, info->nr_cellid, *info->tac); + + prnt("OK\n"); + return 0; +} + +extern int stop_L1(module_id_t gnb_id); +static int stop_modem(char *buf, int debug, telnet_printfunc_t prnt) +{ + if (!running) + ERROR_MSG_RET("cannot stop, nr-softmodem not running\n"); + + /* make UEs out of sync and wait 50ms to ensure no PUCCH is scheduled. After + * a restart, the frame/slot numbers will be different, which "confuses" the + * scheduler, which has many PUCCH structures filled with expected frame/slot + * combinations that won't happen. */ + const gNB_MAC_INST *mac = RC.nrmac[0]; + UE_iterator((NR_UE_info_t **)mac->UE_info.list, it) { + nr_mac_trigger_ul_failure(&it->UE_sched_ctrl, 1); + } + usleep(50000); + + stop_L1(0); + running = false; + prnt("OK\n"); + return 0; +} + +extern int start_L1L2(module_id_t gnb_id); +static int start_modem(char *buf, int debug, telnet_printfunc_t prnt) +{ + if (running) + ERROR_MSG_RET("cannot start, nr-softmodem already running\n"); + start_L1L2(0); + running = true; + prnt("OK\n"); + return 0; +} + +static telnetshell_cmddef_t o1cmds[] = { + {"stats", "", get_stats}, + {"config", "[]", set_config}, + {"bwconfig", "", set_bwconfig}, + {"stop_modem", "", stop_modem}, + {"start_modem", "", start_modem}, + {"", "", NULL}, +}; + +static telnetshell_vardef_t o1vars[] = { + {"", 0, 0, NULL} +}; + +void add_o1_cmds(void) { + add_telnetcmd("o1", o1vars, o1cmds); +} From c71c9de953ea0dd791abc28fe34b5eee26846dee Mon Sep 17 00:00:00 2001 From: Ronak Kanthaliya Date: Fri, 30 Aug 2024 14:26:20 +0530 Subject: [PATCH 29/32] Additional BW configurability on telnet server --- common/utils/telnetsrv/telnetsrv_o1.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/common/utils/telnetsrv/telnetsrv_o1.c b/common/utils/telnetsrv/telnetsrv_o1.c index 36cffdf18d..18cd58aa06 100644 --- a/common/utils/telnetsrv/telnetsrv_o1.c +++ b/common/utils/telnetsrv/telnetsrv_o1.c @@ -323,6 +323,24 @@ static int set_bwconfig(char *buf, int debug, telnet_printfunc_t prnt) frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth = 51; initialUL->locationAndBandwidth = 13750; get_softmodem_params()->threequarter_fs = 0; + } else if (strcmp(buf, "100") == 0) { + *scc->downlinkConfigCommon->frequencyInfoDL->absoluteFrequencySSB = 646668; + frequencyInfoDL->absoluteFrequencyPointA = 643392; + AssertFatal(frequencyInfoUL->absoluteFrequencyPointA == NULL, "only handle TDD\n"); + frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth = 273; + initialDL->locationAndBandwidth = 1099; + frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth = 273; + initialUL->locationAndBandwidth = 1099; + get_softmodem_params()->threequarter_fs = 0; + } else if (strcmp(buf, "60") == 0) { + *scc->downlinkConfigCommon->frequencyInfoDL->absoluteFrequencySSB = 621984; + frequencyInfoDL->absoluteFrequencyPointA = 620040; + AssertFatal(frequencyInfoUL->absoluteFrequencyPointA == NULL, "only handle TDD\n"); + frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth = 162; + initialDL->locationAndBandwidth = 31624; + frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth = 162; + initialUL->locationAndBandwidth = 31624; + get_softmodem_params()->threequarter_fs = 0; } else { ERROR_MSG_RET("unhandled option %s\n", buf); } From 70a746a4454d9e8a0d5e6a05d808e98dcf2917d9 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 5 Dec 2024 13:59:49 +0100 Subject: [PATCH 30/32] Provide O1 telnet interface to remove all UEs --- common/utils/telnetsrv/telnetsrv_o1.c | 9 +++++++++ openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c | 3 ++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/common/utils/telnetsrv/telnetsrv_o1.c b/common/utils/telnetsrv/telnetsrv_o1.c index 18cd58aa06..dc48a477d3 100644 --- a/common/utils/telnetsrv/telnetsrv_o1.c +++ b/common/utils/telnetsrv/telnetsrv_o1.c @@ -391,12 +391,21 @@ static int start_modem(char *buf, int debug, telnet_printfunc_t prnt) return 0; } +extern void du_clear_all_ue_states(); +static int remove_mac_ues(char *buf, int debug, telnet_printfunc_t prnt) +{ + du_clear_all_ue_states(); + prnt("OK\n"); + return 0; +} + static telnetshell_cmddef_t o1cmds[] = { {"stats", "", get_stats}, {"config", "[]", set_config}, {"bwconfig", "", set_bwconfig}, {"stop_modem", "", stop_modem}, {"start_modem", "", start_modem}, + {"remove_mac_ues", "", remove_mac_ues}, {"", "", NULL}, }; 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 1faee06a39..9c6f5f4b27 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c +++ b/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c @@ -99,7 +99,8 @@ static bool check_plmn_identity(const f1ap_plmn_t *check_plmn, const f1ap_plmn_t return plmn->mcc == check_plmn->mcc && plmn->mnc_digit_length == check_plmn->mnc_digit_length && plmn->mnc == check_plmn->mnc; } -static void du_clear_all_ue_states() +/* not static, so we can call it from the outside (in telnet) */ +void du_clear_all_ue_states() { gNB_MAC_INST *mac = RC.nrmac[0]; NR_SCHED_LOCK(&mac->sched_lock); From bcba876fcba1033e07d41248688d25b2d0ef6adc Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Fri, 6 Dec 2024 10:00:21 +0100 Subject: [PATCH 31/32] Do not free inside gnb_du_configuration_update_f1ap() The consumer of this function just passes a pointer to something to be sent. This function should not free this memory, it's simply not it's task. This is the same bug as above in "CU_handle_gNB_DU_CONFIGURATION_UPDATE(): don't free the message of RRC" --- openair2/LAYER2/NR_MAC_gNB/mac_rrc_ul_f1ap.c | 2 -- 1 file changed, 2 deletions(-) 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 99e6f02a57..544ab3889c 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_rrc_ul_f1ap.c +++ b/openair2/LAYER2/NR_MAC_gNB/mac_rrc_ul_f1ap.c @@ -82,8 +82,6 @@ static void gnb_du_configuration_update_f1ap(const f1ap_gnb_du_configuration_upd f1ap_gnb_du_configuration_update_t cp = cp_f1ap_du_configuration_update(upd); /* transfer to ITTI message */ F1AP_GNB_DU_CONFIGURATION_UPDATE(msg) = cp; - /* free after copy */ - free_f1ap_du_configuration_update(upd); /* send to RRC task */ itti_send_msg_to_task(TASK_DU_F1, 0, msg); } From 59e6a33d2bec4becc899aadac17f67cacfe561e5 Mon Sep 17 00:00:00 2001 From: sagar arora Date: Fri, 6 Dec 2024 11:19:50 +0100 Subject: [PATCH 32/32] Add telnet (O1) support modules in gNB image --- docker/Dockerfile.gNB.ubuntu22 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docker/Dockerfile.gNB.ubuntu22 b/docker/Dockerfile.gNB.ubuntu22 index 8d154eeb13..35e442f93d 100644 --- a/docker/Dockerfile.gNB.ubuntu22 +++ b/docker/Dockerfile.gNB.ubuntu22 @@ -95,6 +95,10 @@ COPY --from=gnb-build \ /oai-ran/cmake_targets/ran_build/build/libtelnetsrv.so \ /oai-ran/cmake_targets/ran_build/build/libtelnetsrv_ci.so \ /oai-ran/cmake_targets/ran_build/build/libparams_yaml.so \ + /oai-ran/cmake_targets/ran_build/build/libtelnetsrv_bearer.so \ + /oai-ran/cmake_targets/ran_build/build/libtelnetsrv_5Gue.so \ + /oai-ran/cmake_targets/ran_build/build/libtelnetsrv_rrc.so \ + /oai-ran/cmake_targets/ran_build/build/libtelnetsrv_o1.so \ /usr/local/lib/ # Now we are copying from builder-image the UHD files.