From 5c6d90aaff415f9e928adefce6d418314d070753 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Fri, 31 May 2024 12:30:47 +0200 Subject: [PATCH 01/41] Use NR_timer_t for UE transmission interrupt, e.g., RRC processing Refactor the RRC processing timer to be a generic "transmission interrupt" timer, and use NR_timer_t for it. Also, depending on the kind of RRC processing (see 38.331 table 12.1-1), timing requirements might be different. However, the DU does not always know which RRC processing is ongoing, and does not get this information via F1. Hence, introduce a "worst case" delay time, which aligns with the spec (it says these times are "minima") and is accepted by COTS UE. The gNB MAC might need to stop transmission either for some time (RRC processing), or continuously (transmission stop requested by CU). Those can be handled in one common timer implementation. The continuous case will be used in a follow-up commit. Co-authored-by: Luis Pereira --- openair2/LAYER2/NR_MAC_gNB/config.c | 6 +-- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c | 18 +++---- .../LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 5 +- .../NR_MAC_gNB/gNB_scheduler_primitives.c | 54 ++++++++++--------- .../LAYER2/NR_MAC_gNB/gNB_scheduler_srs.c | 2 +- .../LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c | 4 +- .../LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c | 2 +- openair2/LAYER2/NR_MAC_gNB/mac_proto.h | 4 +- .../LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c | 3 +- openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h | 9 ++-- 10 files changed, 56 insertions(+), 51 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/config.c b/openair2/LAYER2/NR_MAC_gNB/config.c index bfbb5dcfb5..ea4fbed192 100644 --- a/openair2/LAYER2/NR_MAC_gNB/config.c +++ b/openair2/LAYER2/NR_MAC_gNB/config.c @@ -844,9 +844,9 @@ bool nr_mac_prepare_ra_ue(gNB_MAC_INST *nrmac, uint32_t rnti, NR_CellGroupConfig /* Prepare a new CellGroupConfig to be applied for this UE. We cannot * immediatly apply it, as we have to wait for the reconfiguration through RRC. * This function sets up everything to apply the reconfiguration. Later, we - * will trigger the timer with nr_mac_enable_ue_rrc_processing_timer(); upon - * expiry nr_mac_apply_cellgroup() will apply the CellGroupConfig (radio config - * etc). */ + * will trigger the UE inactivity with nr_mac_interrupt_ue_transmission(); upon + * expiry, nr_mac_apply_cellgroup() will apply the CellGroupConfig (radio + * config etc). */ bool nr_mac_prepare_cellgroup_update(gNB_MAC_INST *nrmac, NR_UE_info_t *UE, NR_CellGroupConfig_t *CellGroup) { DevAssert(nrmac != NULL); diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c index d6dfa19dd4..1e437aa63e 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c @@ -1698,12 +1698,8 @@ static void nr_generate_Msg2(module_id_t module_idP, if (ra->cfra) { NR_UE_info_t *UE = find_nr_UE(&RC.nrmac[module_idP]->UE_info, ra->rnti); if (UE) { - const NR_ServingCellConfig_t *servingCellConfig = UE->CellGroup ? UE->CellGroup->spCellConfig->spCellConfigDedicated : NULL; - uint32_t delay_ms = servingCellConfig && servingCellConfig->downlinkBWP_ToAddModList - ? NR_RRC_SETUP_DELAY_MS + NR_RRC_BWP_SWITCHING_DELAY_MS - : NR_RRC_SETUP_DELAY_MS; - NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl; - sched_ctrl->rrc_processing_timer = (delay_ms << ra->DL_BWP.scs); + int delay = nr_mac_get_reconfig_delay_slots(ra->DL_BWP.scs); + nr_mac_interrupt_ue_transmission(RC.nrmac[module_idP], UE, FOLLOW_INSYNC, delay); } } @@ -2248,8 +2244,9 @@ static void nr_generate_Msg4_MsgB(module_id_t module_idP, // 3GPP TS 38.331 Section 12 Table 12.1-1: UE performance requirements for RRC procedures for UEs // Msg4 may transmit a RRCReconfiguration, for example when UE sends RRCReestablishmentComplete and MAC CE for C-RNTI in Msg3. // In that case, gNB will generate a RRCReconfiguration that will be transmitted in Msg4, so we need to apply CellGroup after the Ack, - // UE->apply_cellgroup was already set when processing RRCReestablishment message - nr_mac_enable_ue_rrc_processing_timer(nr_mac, UE, UE->apply_cellgroup); + // UE->reconfigCellGroup was already set when processing RRCReestablishment message + int delay = nr_mac_get_reconfig_delay_slots(UE->current_UL_BWP.scs); + nr_mac_interrupt_ue_transmission(nr_mac, UE, UE->interrupt_action, delay); nr_clear_ra_proc(ra); } else { @@ -2291,8 +2288,9 @@ static void nr_check_Msg4_MsgB_Ack(module_id_t module_id, int CC_id, frame_t fra // 3GPP TS 38.331 Section 12 Table 12.1-1: UE performance requirements for RRC procedures for UEs // Msg4 may transmit a RRCReconfiguration, for example when UE sends RRCReestablishmentComplete and MAC CE for C-RNTI in Msg3. // In that case, gNB will generate a RRCReconfiguration that will be transmitted in Msg4, so we need to apply CellGroup after the Ack, - // UE->apply_cellgroup was already set when processing RRCReestablishment message - nr_mac_enable_ue_rrc_processing_timer(RC.nrmac[module_id], UE, UE->apply_cellgroup); + // UE->reconfigCellGroup already set when processing RRCReestablishment message + int delay = nr_mac_get_reconfig_delay_slots(UE->current_UL_BWP.scs); + nr_mac_interrupt_ue_transmission(RC.nrmac[module_id], UE, UE->interrupt_action, delay); nr_clear_ra_proc(ra); if (sched_ctrl->retrans_dl_harq.head >= 0) { diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index af2a2b24e7..ab9be4a271 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -334,9 +334,8 @@ static void nr_store_dlsch_buffer(module_id_t module_id, frame_t frame, sub_fram const int lcid = c->lcid; const uint16_t rnti = UE->rnti; LOG_D(NR_MAC, "In %s: UE %x: LCID %d\n", __FUNCTION__, rnti, lcid); - if (lcid == DL_SCH_LCID_DTCH && sched_ctrl->rrc_processing_timer > 0) { + if (lcid == DL_SCH_LCID_DTCH && nr_timer_is_active(&sched_ctrl->transm_interrupt)) continue; - } start_meas(&RC.nrmac[module_id]->rlc_status_ind); sched_ctrl->rlc_status[lcid] = mac_rlc_status_ind(module_id, rnti, @@ -985,7 +984,7 @@ void nr_schedule_ue_spec(module_id_t module_id, * Possible improvement: take the periodicity from input file. * If such UE is not scheduled now, it will be by the preprocessor later. * If we add the CE, ta_apply will be reset */ - if (frame == (sched_ctrl->ta_frame + 100) % 1024) { + if (frame == ((sched_ctrl->ta_frame + 100) % 1024) && !nr_timer_is_active(&sched_ctrl->transm_interrupt)) { sched_ctrl->ta_apply = true; /* the timer is reset once TA CE is scheduled */ LOG_D(NR_MAC, "[UE %04x][%d.%d] UL timing alignment procedures: setting flag for Timing Advance command\n", UE->rnti, frame, slot); } diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index 834e42d7a3..d860ec49bd 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -2733,9 +2733,8 @@ void nr_csirs_scheduling(int Mod_idP, frame_t frame, sub_frame_t slot, int n_slo if (UE_info->sched_csirs & (1 << dl_bwp->bwp_id)) continue; NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl; - if (sched_ctrl->rrc_processing_timer > 0) { + if (nr_timer_is_active(&sched_ctrl->transm_interrupt)) continue; - } if (!UE->sc_info.csi_MeasConfig) continue; @@ -2951,15 +2950,15 @@ void nr_csirs_scheduling(int Mod_idP, frame_t frame, sub_frame_t slot, int n_slo static void nr_mac_apply_cellgroup(gNB_MAC_INST *mac, NR_UE_info_t *UE, frame_t frame, sub_frame_t slot) { - LOG_D(NR_MAC, "%4d.%2d RNTI %04x: RRC processing timer expired\n", frame, slot, UE->rnti); + LOG_D(NR_MAC, "%4d.%2d RNTI %04x: UE inactivity timer expired\n", frame, slot, UE->rnti); /* check if there is a new CellGroupConfig to be applied */ - if (UE->apply_cellgroup && UE->reconfigCellGroup != NULL) { - LOG_D(NR_MAC, "%4d.%2d RNTI %04x: Apply CellGroupConfig after RRC processing timer expiry\n", frame, slot, UE->rnti); + if (UE->interrupt_action == FOLLOW_INSYNC_RECONFIG && UE->reconfigCellGroup != NULL) { + LOG_D(NR_MAC, "%4d.%2d RNTI %04x: Apply CellGroupConfig after UE inactivity\n", frame, slot, UE->rnti); ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, UE->CellGroup); UE->CellGroup = UE->reconfigCellGroup; UE->reconfigCellGroup = NULL; - UE->apply_cellgroup = false; + UE->interrupt_action = FOLLOW_INSYNC; if (LOG_DEBUGFLAG(DEBUG_ASN1)) xer_fprint(stdout, &asn_DEF_NR_CellGroupConfig, (const void *)UE->CellGroup); @@ -2987,31 +2986,34 @@ static void nr_mac_apply_cellgroup(gNB_MAC_INST *mac, NR_UE_info_t *UE, frame_t } } -int nr_mac_enable_ue_rrc_processing_timer(gNB_MAC_INST *mac, NR_UE_info_t *UE, bool apply_cellgroup) +int nr_mac_get_reconfig_delay_slots(NR_SubcarrierSpacing_t scs) +{ + /* we previously assumed a specific "slot ahead" value for the PHY processing + * time. However, we cannot always know it (e.g., third-party PHY), so simply + * assume a tentative worst-case slot processing time */ + const uint16_t sl_ahead = 10; + /* 16ms seems to be the most common: See 38.331 Tab 12.1-1 */ + int delay_ms = NR_RRC_RECONFIGURATION_DELAY_MS + NR_RRC_BWP_SWITCHING_DELAY_MS; + return (delay_ms << scs) + sl_ahead; +} + +int nr_mac_interrupt_ue_transmission(gNB_MAC_INST *mac, NR_UE_info_t *UE, interrupt_followup_action_t action, int slots) { DevAssert(mac != NULL); DevAssert(UE != NULL); NR_SCHED_ENSURE_LOCKED(&mac->sched_lock); - const uint16_t sl_ahead = mac->if_inst->sl_ahead; - // TODO: account for BWP switch with NR_RRC_BWP_SWITCHING_DELAY_MS - int delay = NR_RRC_RECONFIGURATION_DELAY_MS; - NR_SubcarrierSpacing_t scs = UE->current_UL_BWP.scs; + nr_timer_setup(&UE->UE_sched_ctrl.transm_interrupt, slots, 1); + nr_timer_start(&UE->UE_sched_ctrl.transm_interrupt); + UE->interrupt_action = action; - UE->UE_sched_ctrl.rrc_processing_timer = (delay << scs) + sl_ahead; - UE->apply_cellgroup = apply_cellgroup; - AssertFatal(!UE->apply_cellgroup || (UE->apply_cellgroup && UE->reconfigCellGroup), - "logic bug: apply_cellgroup %d and UE->reconfigCellGroup %p: did you try to apply a cellGroup, while none is deposited?\n", - UE->apply_cellgroup, - UE->reconfigCellGroup); - - // it might happen that timing advance command should be sent during the RRC - // processing timer. To prevent this, set a variable as if we would have just + // it might happen that timing advance command should be sent during the UE + // inactivity time. To prevent this, set a variable as if we would have just // sent it. This way, another TA command will for sure be sent in some - // frames, after RRC processing timer. + // frames, after the inactivity of the UE. UE->UE_sched_ctrl.ta_frame = (mac->frame - 1 + 1024) % 1024; - LOG_D(NR_MAC, "UE %04x: Activate RRC processing timer (%d ms)\n", UE->rnti, delay); + LOG_D(NR_MAC, "UE %04x: Interrupt UE transmission (%d slots) action %d reconfigCellGroup %p\n", UE->rnti, slots, UE->interrupt_action, UE->reconfigCellGroup); return 0; } @@ -3073,10 +3075,10 @@ void nr_mac_update_timers(module_id_t module_id, /* check if UL failure and trigger release request if necessary */ nr_mac_check_ul_failure(mac, UE->rnti, sched_ctrl); - if (sched_ctrl->rrc_processing_timer > 0) { - sched_ctrl->rrc_processing_timer--; - if (sched_ctrl->rrc_processing_timer == 0) - nr_mac_apply_cellgroup(mac, UE, frame, slot); + if (nr_timer_tick(&sched_ctrl->transm_interrupt)) { + /* expired */ + nr_timer_stop(&sched_ctrl->transm_interrupt); + nr_mac_apply_cellgroup(mac, UE, frame, slot); } } } diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_srs.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_srs.c index 5045d6cbe8..945e078e37 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_srs.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_srs.c @@ -533,7 +533,7 @@ void nr_schedule_srs(int module_id, frame_t frame, int slot) sched_ctrl->sched_srs.srs_scheduled = false; } - if ((sched_ctrl->ul_failure && !get_softmodem_params()->phy_test) || sched_ctrl->rrc_processing_timer > 0) { + if ((sched_ctrl->ul_failure && !get_softmodem_params()->phy_test) || nr_timer_is_active(&sched_ctrl->transm_interrupt)) { continue; } diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c index facef09b52..ee573e89a1 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c @@ -223,7 +223,7 @@ void nr_csi_meas_reporting(int Mod_idP, NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl; NR_UE_UL_BWP_t *ul_bwp = &UE->current_UL_BWP; const int n_slots_frame = nr_slots_per_frame[ul_bwp->scs]; - if ((sched_ctrl->rrc_processing_timer > 0) || (sched_ctrl->ul_failure && !get_softmodem_params()->phy_test)) { + if (nr_timer_is_active(&sched_ctrl->transm_interrupt) || (sched_ctrl->ul_failure && !get_softmodem_params()->phy_test)) { continue; } const NR_CSI_MeasConfig_t *csi_measconfig = UE->sc_info.csi_MeasConfig; @@ -1389,7 +1389,7 @@ void nr_sr_reporting(gNB_MAC_INST *nrmac, frame_t SFN, sub_frame_t slot) NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl; NR_UE_UL_BWP_t *ul_bwp = &UE->current_UL_BWP; const int n_slots_frame = nr_slots_per_frame[ul_bwp->scs]; - if (sched_ctrl->ul_failure || sched_ctrl->rrc_processing_timer > 0) + if (sched_ctrl->ul_failure || nr_timer_is_active(&sched_ctrl->transm_interrupt)) continue; NR_PUCCH_Config_t *pucch_Config = ul_bwp->pucch_Config; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c index 4599349a04..de0ca18ad5 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c @@ -1856,7 +1856,7 @@ static void pf_ul(module_id_t module_id, const bool do_sched = nr_UE_is_to_be_scheduled(scc, 0, UE, sched_pusch->frame, sched_pusch->slot, nrmac->ulsch_max_frame_inactivity); LOG_D(NR_MAC,"pf_ul: do_sched UE %04x => %s\n",UE->rnti,do_sched ? "yes" : "no"); - if ((B == 0 && !do_sched) || (sched_ctrl->rrc_processing_timer > 0)) { + if ((B == 0 && !do_sched) || nr_timer_is_active(&sched_ctrl->transm_interrupt)) { continue; } diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h index da3b612561..e63bbd8f99 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h +++ b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h @@ -53,8 +53,10 @@ void nr_mac_configure_sib19(gNB_MAC_INST *nrmac); bool nr_mac_add_test_ue(gNB_MAC_INST *nrmac, uint32_t rnti, NR_CellGroupConfig_t *CellGroup); bool nr_mac_prepare_ra_ue(gNB_MAC_INST *nrmac, uint32_t rnti, NR_CellGroupConfig_t *CellGroup); +int nr_mac_get_reconfig_delay_slots(NR_SubcarrierSpacing_t scs); + bool nr_mac_prepare_cellgroup_update(gNB_MAC_INST *nrmac, NR_UE_info_t *UE, NR_CellGroupConfig_t *CellGroup); -int nr_mac_enable_ue_rrc_processing_timer(gNB_MAC_INST *mac, NR_UE_info_t *UE, bool apply_cellGroup); +int nr_mac_interrupt_ue_transmission(gNB_MAC_INST *mac, NR_UE_info_t *UE, interrupt_followup_action_t action, int slots); void clear_nr_nfapi_information(gNB_MAC_INST *gNB, int CC_idP, diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c b/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c index 407e5869ce..33c0d5e33e 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c +++ b/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c @@ -728,7 +728,8 @@ void dl_rrc_message_transfer(const f1ap_dl_rrc_message_t *dl_rrc) * the CellGroupConfig. Below, we trigger a timer, and the CellGroupConfig * will be applied after its expiry in nr_mac_apply_cellgroup().*/ NR_SCHED_LOCK(&mac->sched_lock); - nr_mac_enable_ue_rrc_processing_timer(mac, UE, /* apply_cellGroup = */ true); + int delay = nr_mac_get_reconfig_delay_slots(UE->current_UL_BWP.scs); + nr_mac_interrupt_ue_transmission(mac, UE, FOLLOW_INSYNC_RECONFIG, delay); NR_SCHED_UNLOCK(&mac->sched_lock); UE->expect_reconfiguration = false; /* Re-establish RLC for all remaining bearers */ diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h index aa07e8f474..8d97ddc607 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h +++ b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h @@ -43,6 +43,7 @@ #include #include #include "common/utils/ds/seq_arr.h" +#include "common/utils/nr/nr_common.h" #define NR_SCHED_LOCK(lock) \ do { \ @@ -664,8 +665,8 @@ typedef struct { NR_list_t retrans_ul_harq; NR_UE_mac_ce_ctrl_t UE_mac_ce_ctrl; // MAC CE related information - /// Timer for RRC processing procedures - uint32_t rrc_processing_timer; + /// Timer for RRC processing procedures and transmission activity + NR_timer_t transm_interrupt; /// sri, ul_ri and tpmi based on SRS nr_srs_feedback_t srs_feedback; @@ -738,6 +739,8 @@ typedef struct nr_mac_rrc_ul_if_s { initial_ul_rrc_message_transfer_func_t initial_ul_rrc_message_transfer; } nr_mac_rrc_ul_if_t; +typedef enum interrupt_followup_action { FOLLOW_INSYNC, FOLLOW_INSYNC_RECONFIG } interrupt_followup_action_t; + /*! \brief UE list used by gNB to order UEs/CC for scheduling*/ typedef struct { rnti_t rnti; @@ -756,7 +759,7 @@ typedef struct { /// reestablishRLC has to be signaled in RRCreconfiguration bool reestablish_rlc; NR_CellGroupConfig_t *reconfigCellGroup; - bool apply_cellgroup; + interrupt_followup_action_t interrupt_action; NR_UE_NR_Capability_t *capability; // UE selected beam index uint8_t UE_beam_index; From d70561d509dd7c838222ab1c13225ac50f5b7930 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Fri, 17 May 2024 17:57:47 +0200 Subject: [PATCH 02/41] Implement F1 UE Ctxt HandoverPrepInfo in F1 Implement the HandoverPreparationInformation F1AP forwarding logic, and add it to the F1 internal representation structure. No logic is implemented in this commit. Co-authored-by: batuhanduyuler Co-authored-by: rmagueta --- openair2/COMMON/f1ap_messages_types.h | 2 ++ openair2/F1AP/f1ap_cu_ue_context_management.c | 15 ++++++++++ openair2/F1AP/f1ap_du_ue_context_management.c | 30 ++++++++++++++++--- openair2/RRC/NR/mac_rrc_dl_f1ap.c | 9 +++++- 4 files changed, 51 insertions(+), 5 deletions(-) diff --git a/openair2/COMMON/f1ap_messages_types.h b/openair2/COMMON/f1ap_messages_types.h index f159e63ca3..f357fae2d8 100644 --- a/openair2/COMMON/f1ap_messages_types.h +++ b/openair2/COMMON/f1ap_messages_types.h @@ -442,6 +442,8 @@ typedef struct cu_to_du_rrc_information_s { uint32_t uE_CapabilityRAT_ContainerList_length; uint8_t * measConfig; uint32_t measConfig_length; + uint8_t *handoverPreparationInfo; + uint32_t handoverPreparationInfo_length; }cu_to_du_rrc_information_t; typedef struct du_to_cu_rrc_information_s { diff --git a/openair2/F1AP/f1ap_cu_ue_context_management.c b/openair2/F1AP/f1ap_cu_ue_context_management.c index 65c34a10c1..ca6c939a3a 100644 --- a/openair2/F1AP/f1ap_cu_ue_context_management.c +++ b/openair2/F1AP/f1ap_cu_ue_context_management.c @@ -276,6 +276,21 @@ int CU_send_UE_CONTEXT_SETUP_REQUEST(sctp_assoc_t assoc_id, f1ap_ue_context_setu OCTET_STRING_fromBuf(measConfig, (const char*)f1ap_ue_context_setup_req->cu_to_du_rrc_information->measConfig, f1ap_ue_context_setup_req->cu_to_du_rrc_information->measConfig_length); } + + /* optional */ + /* HandoverPreparationInformation */ + if (f1ap_ue_context_setup_req->cu_to_du_rrc_information->handoverPreparationInfo_length > 0) { + DevAssert(f1ap_ue_context_setup_req->cu_to_du_rrc_information->handoverPreparationInfo != NULL); + F1AP_ProtocolExtensionContainer_10696P60_t *p = calloc(1, sizeof(*p)); + ie6->value.choice.CUtoDURRCInformation.iE_Extensions = (struct F1AP_ProtocolExtensionContainer *)p; + asn1cSequenceAdd(p->list, F1AP_CUtoDURRCInformation_ExtIEs_t, ie_ext); + ie_ext->id = F1AP_ProtocolIE_ID_id_HandoverPreparationInformation; + ie_ext->criticality = F1AP_Criticality_ignore; + ie_ext->extensionValue.present = F1AP_CUtoDURRCInformation_ExtIEs__extensionValue_PR_HandoverPreparationInformation; + OCTET_STRING_fromBuf(&ie_ext->extensionValue.choice.HandoverPreparationInformation, + (const char *)f1ap_ue_context_setup_req->cu_to_du_rrc_information->handoverPreparationInfo, + f1ap_ue_context_setup_req->cu_to_du_rrc_information->handoverPreparationInfo_length); + } } /* optional */ diff --git a/openair2/F1AP/f1ap_du_ue_context_management.c b/openair2/F1AP/f1ap_du_ue_context_management.c index ad728d5c89..8844e79baf 100644 --- a/openair2/F1AP/f1ap_du_ue_context_management.c +++ b/openair2/F1AP/f1ap_du_ue_context_management.c @@ -171,12 +171,34 @@ int DU_handle_UE_CONTEXT_SETUP_REQUEST(instance_t instance, sctp_assoc_t assoc_i F1AP_ProtocolIE_ID_id_CUtoDURRCInformation, false); if(ieCuRrcInfo!=NULL){ f1ap_ue_context_setup_req->cu_to_du_rrc_information = (cu_to_du_rrc_information_t *)calloc(1,sizeof(cu_to_du_rrc_information_t)); - if(ieCuRrcInfo->value.choice.CUtoDURRCInformation.uE_CapabilityRAT_ContainerList!=NULL){ - f1ap_ue_context_setup_req->cu_to_du_rrc_information->uE_CapabilityRAT_ContainerList = (uint8_t *)calloc(1,ieCuRrcInfo->value.choice.CUtoDURRCInformation.uE_CapabilityRAT_ContainerList->size); - memcpy(f1ap_ue_context_setup_req->cu_to_du_rrc_information->uE_CapabilityRAT_ContainerList, ieCuRrcInfo->value.choice.CUtoDURRCInformation.uE_CapabilityRAT_ContainerList->buf, ieCuRrcInfo->value.choice.CUtoDURRCInformation.uE_CapabilityRAT_ContainerList->size); - f1ap_ue_context_setup_req->cu_to_du_rrc_information->uE_CapabilityRAT_ContainerList_length = ieCuRrcInfo->value.choice.CUtoDURRCInformation.uE_CapabilityRAT_ContainerList->size; + cu_to_du_rrc_information_t *cu2du = f1ap_ue_context_setup_req->cu_to_du_rrc_information; + const F1AP_CUtoDURRCInformation_t *cu2duie = &ieCuRrcInfo->value.choice.CUtoDURRCInformation; + if(cu2duie->uE_CapabilityRAT_ContainerList!=NULL){ + cu2du->uE_CapabilityRAT_ContainerList = calloc(1, cu2duie->uE_CapabilityRAT_ContainerList->size); + memcpy(cu2du->uE_CapabilityRAT_ContainerList, + cu2duie->uE_CapabilityRAT_ContainerList->buf, + cu2duie->uE_CapabilityRAT_ContainerList->size); + cu2du->uE_CapabilityRAT_ContainerList_length = cu2duie->uE_CapabilityRAT_ContainerList->size; LOG_D(F1AP, "Size f1ap_ue_context_setup_req->cu_to_du_rrc_information->uE_CapabilityRAT_ContainerList_length: %d \n", f1ap_ue_context_setup_req->cu_to_du_rrc_information->uE_CapabilityRAT_ContainerList_length); } + if (cu2duie->iE_Extensions != NULL) { + const F1AP_ProtocolExtensionContainer_10696P60_t *ext = (const F1AP_ProtocolExtensionContainer_10696P60_t *)cu2duie->iE_Extensions; + for (int i = 0; i < ext->list.count; ++i) { + const F1AP_CUtoDURRCInformation_ExtIEs_t *cu2du_info = ext->list.array[i]; + switch (cu2du_info->id) { + case F1AP_ProtocolIE_ID_id_HandoverPreparationInformation: + DevAssert(cu2du_info->extensionValue.present == F1AP_CUtoDURRCInformation_ExtIEs__extensionValue_PR_HandoverPreparationInformation); + const F1AP_HandoverPreparationInformation_t *hopi = &cu2du_info->extensionValue.choice.MeasurementTimingConfiguration; + cu2du->handoverPreparationInfo = calloc_or_fail(1, hopi->size); + memcpy(cu2du->handoverPreparationInfo, hopi->buf, hopi->size); + cu2du->handoverPreparationInfo_length = hopi->size; + break; + default: + LOG_W(F1AP, "unsupported CUtoDURRCInformation_ExtIE %ld encountered, ignoring\n", cu2du_info->id); + break; + } + } + } } /* DRB */ diff --git a/openair2/RRC/NR/mac_rrc_dl_f1ap.c b/openair2/RRC/NR/mac_rrc_dl_f1ap.c index e2e094d866..a802e7ab4d 100644 --- a/openair2/RRC/NR/mac_rrc_dl_f1ap.c +++ b/openair2/RRC/NR/mac_rrc_dl_f1ap.c @@ -79,8 +79,8 @@ static void ue_context_setup_request_f1ap(sctp_assoc_t assoc_id, const f1ap_ue_c AssertFatal(f1ap_msg->cu_to_du_rrc_information != NULL, "out of memory\n"); AssertFatal(req->cu_to_du_rrc_information->cG_ConfigInfo == NULL && req->cu_to_du_rrc_information->cG_ConfigInfo_length == 0, "cg_ConfigInfo not implemented\n"); AssertFatal(req->cu_to_du_rrc_information->measConfig == NULL && req->cu_to_du_rrc_information->measConfig_length == 0, "cg_ConfigInfo not implemented\n"); + const cu_to_du_rrc_information_t *du2cu_req = req->cu_to_du_rrc_information; if (req->cu_to_du_rrc_information->uE_CapabilityRAT_ContainerList != NULL) { - const cu_to_du_rrc_information_t *du2cu_req = req->cu_to_du_rrc_information; cu_to_du_rrc_information_t* du2cu_new = f1ap_msg->cu_to_du_rrc_information; DevAssert(du2cu_req->uE_CapabilityRAT_ContainerList_length > 0); du2cu_new->uE_CapabilityRAT_ContainerList_length = du2cu_req->uE_CapabilityRAT_ContainerList_length; @@ -88,6 +88,13 @@ static void ue_context_setup_request_f1ap(sctp_assoc_t assoc_id, const f1ap_ue_c AssertFatal(du2cu_new->uE_CapabilityRAT_ContainerList != NULL, "out of memory\n"); memcpy(du2cu_new->uE_CapabilityRAT_ContainerList, du2cu_req->uE_CapabilityRAT_ContainerList, du2cu_new->uE_CapabilityRAT_ContainerList_length); } + if (req->cu_to_du_rrc_information->handoverPreparationInfo != NULL) { + cu_to_du_rrc_information_t *du2cu_new = f1ap_msg->cu_to_du_rrc_information; + DevAssert(du2cu_req->handoverPreparationInfo_length > 0); + du2cu_new->handoverPreparationInfo_length = du2cu_req->handoverPreparationInfo_length; + du2cu_new->handoverPreparationInfo = malloc_or_fail(du2cu_new->handoverPreparationInfo_length); + memcpy(du2cu_new->handoverPreparationInfo, du2cu_req->handoverPreparationInfo, du2cu_new->handoverPreparationInfo_length); + } } if (req->drbs_to_be_setup_length > 0) { int n = req->drbs_to_be_setup_length; From 004403c84d57e8c0efb253e267b63a39b2cead63 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 23 May 2024 18:00:15 +0200 Subject: [PATCH 03/41] Implement F1 UE Ctxt Modif Transmission Action Indicator Implement the Transmission Action Indicator F1AP forwarding logic, and add it to the F1 internal representation structure. After the transmission interrupt timer (during which SRB1 can send messages), the UE will be out-of-sync (we assume it's not there anymore). Co-authored-by: batuhanduyuler Co-authored-by: rmagueta --- openair2/COMMON/f1ap_messages_types.h | 6 ++++++ openair2/F1AP/f1ap_cu_ue_context_management.c | 7 +++++-- openair2/F1AP/f1ap_du_ue_context_management.c | 15 +++++++++++++++ .../LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c | 13 ++++++++++++- openair2/LAYER2/NR_MAC_gNB/mac_proto.h | 1 + openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c | 5 +++++ openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h | 2 +- openair2/RRC/NR/mac_rrc_dl_f1ap.c | 5 +++++ 8 files changed, 50 insertions(+), 4 deletions(-) diff --git a/openair2/COMMON/f1ap_messages_types.h b/openair2/COMMON/f1ap_messages_types.h index f357fae2d8..569f3857c0 100644 --- a/openair2/COMMON/f1ap_messages_types.h +++ b/openair2/COMMON/f1ap_messages_types.h @@ -466,6 +466,11 @@ typedef enum ReconfigurationCompl_e { RRCreconf_success = 2, } ReconfigurationCompl_t; +typedef enum TransmActionInd_e { + TransmActionInd_STOP, + TransmActionInd_RESTART, +} TransmActionInd_t; + typedef struct f1ap_ue_context_setup_s { uint32_t gNB_CU_ue_id; uint32_t gNB_DU_ue_id; @@ -496,6 +501,7 @@ typedef struct f1ap_ue_context_setup_s { ReconfigurationCompl_t ReconfigComplOutcome; uint8_t *rrc_container; int rrc_container_length; + TransmActionInd_t *transm_action_ind; } f1ap_ue_context_setup_t, f1ap_ue_context_modif_req_t, f1ap_ue_context_modif_resp_t; typedef enum F1ap_Cause_e { diff --git a/openair2/F1AP/f1ap_cu_ue_context_management.c b/openair2/F1AP/f1ap_cu_ue_context_management.c index ca6c939a3a..924c9ecfca 100644 --- a/openair2/F1AP/f1ap_cu_ue_context_management.c +++ b/openair2/F1AP/f1ap_cu_ue_context_management.c @@ -1023,12 +1023,15 @@ int CU_send_UE_CONTEXT_MODIFICATION_REQUEST(sctp_assoc_t assoc_id, f1ap_ue_conte /* optional */ /* c7. TransmissionActionIndicator */ - if (0) { + if (f1ap_ue_context_modification_req->transm_action_ind != NULL) { asn1cSequenceAdd(out->protocolIEs.list, F1AP_UEContextModificationRequestIEs_t, ie7); ie7->id = F1AP_ProtocolIE_ID_id_TransmissionActionIndicator; ie7->criticality = F1AP_Criticality_ignore; ie7->value.present = F1AP_UEContextModificationRequestIEs__value_PR_TransmissionActionIndicator; - ie7->value.choice.TransmissionActionIndicator = F1AP_TransmissionActionIndicator_stop; + ie7->value.choice.TransmissionActionIndicator = *f1ap_ue_context_modification_req->transm_action_ind; + // Stop is guaranteed to be 0, by restart might be different from 1 + static_assert((int)F1AP_TransmissionActionIndicator_restart == (int)TransmActionInd_RESTART, + "mismatch of ASN.1 and internal representation\n"); } /* optional */ diff --git a/openair2/F1AP/f1ap_du_ue_context_management.c b/openair2/F1AP/f1ap_du_ue_context_management.c index 8844e79baf..18a04962ad 100644 --- a/openair2/F1AP/f1ap_du_ue_context_management.c +++ b/openair2/F1AP/f1ap_du_ue_context_management.c @@ -911,6 +911,21 @@ int DU_handle_UE_CONTEXT_MODIFICATION_REQUEST(instance_t instance, sctp_assoc_t F1AP_FIND_PROTOCOLIE_BY_ID(F1AP_UEContextModificationRequestIEs_t, ieSrb, container, F1AP_ProtocolIE_ID_id_SRBs_ToBeSetupMod_List, false); + F1AP_UEContextModificationRequestIEs_t *ieTxInd; + F1AP_FIND_PROTOCOLIE_BY_ID(F1AP_UEContextModificationRequestIEs_t, + ieTxInd, + container, + F1AP_ProtocolIE_ID_id_TransmissionActionIndicator, + false); + + if (ieTxInd != NULL) { + f1ap_ue_context_modification_req->transm_action_ind = calloc_or_fail(1, sizeof(*f1ap_ue_context_modification_req->transm_action_ind)); + *f1ap_ue_context_modification_req->transm_action_ind = ieTxInd->value.choice.TransmissionActionIndicator; + // Stop is guaranteed to be 0, by restart might be different from 1 + static_assert((int)F1AP_TransmissionActionIndicator_restart == (int)TransmActionInd_RESTART, + "mismatch of ASN.1 and internal representation\n"); + } + if(ieSrb != NULL) { f1ap_ue_context_modification_req->srbs_to_be_setup_length = ieSrb->value.choice.SRBs_ToBeSetupMod_List.list.count; f1ap_ue_context_modification_req->srbs_to_be_setup = calloc(f1ap_ue_context_modification_req->srbs_to_be_setup_length, diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index d860ec49bd..5ae2eb8607 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -3017,6 +3017,14 @@ int nr_mac_interrupt_ue_transmission(gNB_MAC_INST *mac, NR_UE_info_t *UE, interr return 0; } +int nr_transmission_action_indicator_stop(gNB_MAC_INST *mac, NR_UE_info_t *UE_info) +{ + int delay = nr_mac_get_reconfig_delay_slots(UE_info->current_DL_BWP.scs); + nr_mac_interrupt_ue_transmission(mac, UE_info, FOLLOW_OUTOFSYNC, delay); + LOG_I(NR_MAC, "gNB-DU received the TransmissionActionIndicator with Stop value for UE %04x\n", UE_info->rnti); + return 0; +} + void nr_mac_trigger_release_complete(gNB_MAC_INST *mac, int rnti) { // the CU might not know such UE, e.g., because we never sent a message to @@ -3078,7 +3086,10 @@ void nr_mac_update_timers(module_id_t module_id, if (nr_timer_tick(&sched_ctrl->transm_interrupt)) { /* expired */ nr_timer_stop(&sched_ctrl->transm_interrupt); - nr_mac_apply_cellgroup(mac, UE, frame, slot); + if (UE->interrupt_action == FOLLOW_OUTOFSYNC) + nr_mac_trigger_ul_failure(sched_ctrl, UE->current_DL_BWP.scs); + else + nr_mac_apply_cellgroup(mac, UE, frame, slot); } } } diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h index e63bbd8f99..4ee100f812 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h +++ b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h @@ -57,6 +57,7 @@ int nr_mac_get_reconfig_delay_slots(NR_SubcarrierSpacing_t scs); bool nr_mac_prepare_cellgroup_update(gNB_MAC_INST *nrmac, NR_UE_info_t *UE, NR_CellGroupConfig_t *CellGroup); int nr_mac_interrupt_ue_transmission(gNB_MAC_INST *mac, NR_UE_info_t *UE, interrupt_followup_action_t action, int slots); +int nr_transmission_action_indicator_stop(gNB_MAC_INST *mac, NR_UE_info_t *UE_info); void clear_nr_nfapi_information(gNB_MAC_INST *gNB, int CC_idP, 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 33c0d5e33e..a69ab2732e 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c +++ b/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c @@ -588,6 +588,11 @@ void ue_context_modification_request(const f1ap_ue_context_modif_req_t *req) } else { ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, new_CellGroup); // we actually don't need it } + + if (req->transm_action_ind != NULL) { + AssertFatal(*req->transm_action_ind == TransmActionInd_STOP, "Transmission Action Indicator restart not handled yet\n"); + nr_transmission_action_indicator_stop(mac, UE); + } NR_SCHED_UNLOCK(&mac->sched_lock); /* some sanity checks, since we use the same type for request and response */ diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h index 8d97ddc607..e0cf53c5a0 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h +++ b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h @@ -739,7 +739,7 @@ typedef struct nr_mac_rrc_ul_if_s { initial_ul_rrc_message_transfer_func_t initial_ul_rrc_message_transfer; } nr_mac_rrc_ul_if_t; -typedef enum interrupt_followup_action { FOLLOW_INSYNC, FOLLOW_INSYNC_RECONFIG } interrupt_followup_action_t; +typedef enum interrupt_followup_action { FOLLOW_INSYNC, FOLLOW_INSYNC_RECONFIG, FOLLOW_OUTOFSYNC } interrupt_followup_action_t; /*! \brief UE list used by gNB to order UEs/CC for scheduling*/ typedef struct { diff --git a/openair2/RRC/NR/mac_rrc_dl_f1ap.c b/openair2/RRC/NR/mac_rrc_dl_f1ap.c index a802e7ab4d..70d36a144f 100644 --- a/openair2/RRC/NR/mac_rrc_dl_f1ap.c +++ b/openair2/RRC/NR/mac_rrc_dl_f1ap.c @@ -168,6 +168,11 @@ static void ue_context_modification_request_f1ap(sctp_assoc_t assoc_id, const f1 f1ap_msg->rrc_container_length = req->rrc_container_length; memcpy(f1ap_msg->rrc_container, req->rrc_container, req->rrc_container_length); } + if (req->transm_action_ind != NULL) { + f1ap_msg->transm_action_ind = malloc(sizeof *f1ap_msg->transm_action_ind); + AssertFatal(f1ap_msg->transm_action_ind != NULL, "out of memory\n"); + *f1ap_msg->transm_action_ind = *req->transm_action_ind; + } itti_send_msg_to_task(TASK_CU_F1, 0, msg); } From c854b9cebb4f380f4b0b4c139eeaa264ab51d18b Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 23 May 2024 18:53:57 +0200 Subject: [PATCH 04/41] get_UE_waiting_CFRA_msg3(): check that RA process is for given RNTI Co-authored-by: batuhanduyuler --- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c index de0ca18ad5..5dc88739a5 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c @@ -561,12 +561,16 @@ static void abort_nr_ul_harq(NR_UE_info_t *UE, int8_t harq_pid) sched_ctrl->sched_ul_bytes = 0; } -static bool get_UE_waiting_CFRA_msg3(const gNB_MAC_INST *gNB_mac, const int CC_id, const frame_t frame, const sub_frame_t slot) +static bool get_UE_waiting_CFRA_msg3(const gNB_MAC_INST *gNB_mac, + const int CC_id, + const frame_t frame, + const sub_frame_t slot, + rnti_t rnti) { bool UE_waiting_CFRA_msg3 = false; for (int i = 0; i < NR_NB_RA_PROC_MAX; i++) { const NR_RA_t *ra = &gNB_mac->common_channels[CC_id].ra[i]; - if (ra->cfra == true && ra->ra_state == nrRA_WAIT_Msg3 && frame == ra->Msg3_frame && slot == ra->Msg3_slot) { + if (ra->cfra && ra->ra_state == nrRA_WAIT_Msg3 && frame == ra->Msg3_frame && slot == ra->Msg3_slot && rnti == ra->rnti) { UE_waiting_CFRA_msg3 = true; break; } @@ -584,7 +588,7 @@ void handle_nr_ul_harq(const int CC_idP, NR_SCHED_LOCK(&nrmac->sched_lock); NR_UE_info_t *UE = find_nr_UE(&nrmac->UE_info, crc_pdu->rnti); - bool UE_waiting_CFRA_msg3 = get_UE_waiting_CFRA_msg3(nrmac, CC_idP, frame, slot); + bool UE_waiting_CFRA_msg3 = get_UE_waiting_CFRA_msg3(nrmac, CC_idP, frame, slot, crc_pdu->rnti); if (!UE || UE_waiting_CFRA_msg3 == true) { LOG_D(NR_MAC, "handle harq for rnti %04x, in RA process\n", crc_pdu->rnti); @@ -700,7 +704,7 @@ static void _nr_rx_sdu(const module_id_t gnb_mod_idP, const int pusch_failure_thres = gNB_mac->pusch_failure_thres; NR_UE_info_t *UE = find_nr_UE(&gNB_mac->UE_info, current_rnti); - bool UE_waiting_CFRA_msg3 = get_UE_waiting_CFRA_msg3(gNB_mac, CC_idP, frameP, slotP); + bool UE_waiting_CFRA_msg3 = get_UE_waiting_CFRA_msg3(gNB_mac, CC_idP, frameP, slotP, current_rnti); if (UE && UE_waiting_CFRA_msg3 == false) { From fef589cf88848dadd72fbb88ce896ec7d4143bc9 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 23 May 2024 19:23:08 +0200 Subject: [PATCH 05/41] Refactor reconfigurationWithSync ASN.1 to separate function Will be reused later for handover. Co-authored-by: batuhanduyuler --- openair2/RRC/NR/nr_rrc_config.c | 82 +++++++++++++++++---------------- openair2/RRC/NR/nr_rrc_config.h | 2 + 2 files changed, 44 insertions(+), 40 deletions(-) diff --git a/openair2/RRC/NR/nr_rrc_config.c b/openair2/RRC/NR/nr_rrc_config.c index 201a0e84d4..dab37f6f1a 100644 --- a/openair2/RRC/NR/nr_rrc_config.c +++ b/openair2/RRC/NR/nr_rrc_config.c @@ -3293,46 +3293,8 @@ NR_CellGroupConfig_t *get_default_secondaryCellGroup(const NR_ServingCellConfigC secondaryCellGroup->spCellConfig->reconfigurationWithSync = calloc(1, sizeof(*secondaryCellGroup->spCellConfig->reconfigurationWithSync)); - NR_ReconfigurationWithSync_t *reconfigurationWithSync = secondaryCellGroup->spCellConfig->reconfigurationWithSync; - reconfigurationWithSync->spCellConfigCommon = clone_ServingCellConfigCommon(servingcellconfigcommon); - reconfigurationWithSync->newUE_Identity = - (get_softmodem_params()->phy_test == 1) ? 0x1234 : (taus() & 0xffff); - reconfigurationWithSync->t304 = NR_ReconfigurationWithSync__t304_ms2000; - reconfigurationWithSync->rach_ConfigDedicated = NULL; - reconfigurationWithSync->ext1 = NULL; - - // For 2-step contention-free random access procedure - reconfigurationWithSync->rach_ConfigDedicated = calloc(1, sizeof(*reconfigurationWithSync->rach_ConfigDedicated)); - reconfigurationWithSync->rach_ConfigDedicated->present = NR_ReconfigurationWithSync__rach_ConfigDedicated_PR_uplink; - reconfigurationWithSync->rach_ConfigDedicated->choice.uplink = calloc(1, sizeof(struct NR_RACH_ConfigDedicated)); - reconfigurationWithSync->rach_ConfigDedicated->choice.uplink->cfra = calloc(1, sizeof(struct NR_CFRA)); - reconfigurationWithSync->rach_ConfigDedicated->choice.uplink->ra_Prioritization = NULL; - reconfigurationWithSync->rach_ConfigDedicated->choice.uplink->cfra->occasions = calloc(1, sizeof(struct NR_CFRA__occasions)); - memcpy(&reconfigurationWithSync->rach_ConfigDedicated->choice.uplink->cfra->occasions->rach_ConfigGeneric, - &servingcellconfigcommon->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->rach_ConfigGeneric, - sizeof(NR_RACH_ConfigGeneric_t)); - asn1cCallocOne(reconfigurationWithSync->rach_ConfigDedicated->choice.uplink->cfra->occasions->ssb_perRACH_Occasion, - NR_CFRA__occasions__ssb_perRACH_Occasion_one); - reconfigurationWithSync->rach_ConfigDedicated->choice.uplink->cfra->resources.present = NR_CFRA__resources_PR_ssb; - reconfigurationWithSync->rach_ConfigDedicated->choice.uplink->cfra->resources.choice.ssb = - calloc(1, sizeof(struct NR_CFRA__resources__ssb)); - reconfigurationWithSync->rach_ConfigDedicated->choice.uplink->cfra->resources.choice.ssb->ra_ssb_OccasionMaskIndex = 0; - - int n_ssb = 0; - struct NR_CFRA_SSB_Resource *ssbElem[64]; - for (int i = 0; i < 64; i++) { - if ((bitmap >> (63 - i)) & 0x01) { - ssbElem[n_ssb] = calloc(1, sizeof(struct NR_CFRA_SSB_Resource)); - ssbElem[n_ssb]->ssb = i; - ssbElem[n_ssb]->ra_PreambleIndex = 63 - (uid % 64); - asn1cSeqAdd(&secondaryCellGroup->spCellConfig->reconfigurationWithSync->rach_ConfigDedicated->choice.uplink->cfra->resources - .choice.ssb->ssb_ResourceList.list, - ssbElem[n_ssb]); - n_ssb++; - } - } - - secondaryCellGroup->spCellConfig->reconfigurationWithSync->rach_ConfigDedicated->choice.uplink->cfra->ext1 = NULL; + rnti_t rnti = get_softmodem_params()->phy_test == 1 ? 0x1234 : (taus() & 0xffff); + secondaryCellGroup->spCellConfig->reconfigurationWithSync = get_reconfiguration_with_sync(rnti, uid, servingcellconfigcommon); secondaryCellGroup->spCellConfig->rlf_TimersAndConstants = calloc(1, sizeof(*secondaryCellGroup->spCellConfig->rlf_TimersAndConstants)); @@ -3602,3 +3564,43 @@ NR_CellGroupConfig_t *get_default_secondaryCellGroup(const NR_ServingCellConfigC } return secondaryCellGroup; } + +NR_ReconfigurationWithSync_t *get_reconfiguration_with_sync(rnti_t rnti, uid_t uid, const NR_ServingCellConfigCommon_t *scc) +{ + NR_ReconfigurationWithSync_t *reconfigurationWithSync = calloc(1, sizeof(*reconfigurationWithSync)); + reconfigurationWithSync->newUE_Identity = rnti; + reconfigurationWithSync->t304 = NR_ReconfigurationWithSync__t304_ms2000; + reconfigurationWithSync->rach_ConfigDedicated = NULL; + reconfigurationWithSync->ext1 = NULL; + + reconfigurationWithSync->spCellConfigCommon = clone_ServingCellConfigCommon(scc); + + reconfigurationWithSync->rach_ConfigDedicated = calloc(1, sizeof(*reconfigurationWithSync->rach_ConfigDedicated)); + reconfigurationWithSync->rach_ConfigDedicated->present = NR_ReconfigurationWithSync__rach_ConfigDedicated_PR_uplink; + NR_RACH_ConfigDedicated_t *uplink = calloc(1, sizeof(*uplink)); + reconfigurationWithSync->rach_ConfigDedicated->choice.uplink = uplink; + uplink->ra_Prioritization = NULL; + uplink->cfra = calloc(1, sizeof(struct NR_CFRA)); + uplink->cfra->ext1 = NULL; + uplink->cfra->occasions = calloc(1, sizeof(struct NR_CFRA__occasions)); + memcpy(&uplink->cfra->occasions->rach_ConfigGeneric, + &scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->rach_ConfigGeneric, + sizeof(NR_RACH_ConfigGeneric_t)); + asn1cCallocOne(uplink->cfra->occasions->ssb_perRACH_Occasion, NR_CFRA__occasions__ssb_perRACH_Occasion_one); + uplink->cfra->resources.present = NR_CFRA__resources_PR_ssb; + uplink->cfra->resources.choice.ssb = calloc(1, sizeof(struct NR_CFRA__resources__ssb)); + uplink->cfra->resources.choice.ssb->ra_ssb_OccasionMaskIndex = 0; + + uint64_t bitmap = get_ssb_bitmap(scc); + for (int i = 0; i < 64; i++) { + if (((bitmap >> (63 - i)) & 0x01) == 0) + continue; + + NR_CFRA_SSB_Resource_t *ssbElem = calloc(1, sizeof(*ssbElem)); + ssbElem->ssb = i; + ssbElem->ra_PreambleIndex = 63 - (uid % 64); + asn1cSeqAdd(&uplink->cfra->resources.choice.ssb->ssb_ResourceList.list, ssbElem); + } + + return reconfigurationWithSync; +} diff --git a/openair2/RRC/NR/nr_rrc_config.h b/openair2/RRC/NR/nr_rrc_config.h index 87640834e5..ff94f2ab7e 100644 --- a/openair2/RRC/NR/nr_rrc_config.h +++ b/openair2/RRC/NR/nr_rrc_config.h @@ -102,4 +102,6 @@ NR_CellGroupConfig_t *get_default_secondaryCellGroup(const NR_ServingCellConfigC const nr_mac_config_t *configuration, int uid); +NR_ReconfigurationWithSync_t *get_reconfiguration_with_sync(rnti_t rnti, uid_t uid, const NR_ServingCellConfigCommon_t *scc); + #endif From 5c2b72565416c70d2d52b7fcaf58abb117d04e27 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Fri, 24 May 2024 14:43:57 +0200 Subject: [PATCH 06/41] Refactor new-RNTI functionality in separate function This function will be used in the case of a handover for getting a new RNTI (checking that we do not reuse an already used RNTI). --- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c | 27 +++---------------- .../NR_MAC_gNB/gNB_scheduler_primitives.c | 25 +++++++++++++++++ openair2/LAYER2/NR_MAC_gNB/mac_proto.h | 2 ++ 3 files changed, 30 insertions(+), 24 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c index 1e437aa63e..061ad1ba27 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c @@ -40,7 +40,6 @@ #include "common/utils/LOG/vcd_signal_dumper.h" #include "common/utils/nr/nr_common.h" #include "UTIL/OPT/opt.h" -#include "SIMULATION/TOOLS/sim.h" // for taus /* rlc */ #include "openair2/LAYER2/nr_rlc/nr_rlc_oai_api.h" @@ -677,16 +676,6 @@ static NR_RA_t *find_free_nr_RA(NR_RA_t *ra_base, int ra_count, uint16_t preambl return NULL; } -static const NR_RA_t *find_nr_RA_rnti(const NR_RA_t *ra_base, int ra_count, rnti_t rnti) -{ - for (int i = 0; i < ra_count; ++i) { - const NR_RA_t *ra = &ra_base[i]; - if (ra->ra_state != nrRA_gNB_IDLE && ra->rnti == rnti) - return ra; - } - return NULL; -} - void nr_initiate_ra_proc(module_id_t module_idP, int CC_id, frame_t frameP, @@ -712,23 +701,13 @@ void nr_initiate_ra_proc(module_id_t module_idP, } if (ra->rnti == 0) { // This condition allows for the usage of a preconfigured rnti for the CFRA - int loop = 0; - bool exist_connected_ue, exist_in_pending_ra_ue; - rnti_t trial = 0; - do { - // 3GPP TS 38.321 version 15.13.0 Section 7.1 Table 7.1-1: RNTI values - while (trial < 1 || trial > 0xffef) - trial = (taus() % 0xffef) + 1; - exist_connected_ue = find_nr_UE(&nr_mac->UE_info, trial) != NULL; - exist_in_pending_ra_ue = find_nr_RA_rnti(cc->ra, sizeofArray(cc->ra), ra->rnti) != NULL; - loop++; - } while (loop < 100 && (exist_connected_ue || exist_in_pending_ra_ue) ); - if (loop == 100) { + bool rnti_found = nr_mac_get_new_rnti(&nr_mac->UE_info, cc->ra, sizeofArray(cc->ra), &ra->rnti); + if (!rnti_found) { LOG_E(NR_MAC, "initialisation random access: no more available RNTIs for new UE\n"); + nr_clear_ra_proc(ra); NR_SCHED_UNLOCK(&nr_mac->sched_lock); return; } - ra->rnti = trial; } ra->preamble_frame = frameP; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index 5ae2eb8607..b091cf69ee 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -53,6 +53,8 @@ #include "uper_encoder.h" #include "uper_decoder.h" +#include "SIMULATION/TOOLS/sim.h" // for taus + #define ENABLE_MAC_PAYLOAD_DEBUG #define DEBUG_gNB_SCHEDULER 1 @@ -3395,3 +3397,26 @@ bool nr_mac_remove_lcid(NR_UE_sched_ctrl_t *sched_ctrl, long lcid) seq_arr_erase(&sched_ctrl->lc_config, elm.it); return true; } + +static const NR_RA_t *find_nr_RA_rnti(const NR_RA_t *ra_base, int ra_count, rnti_t rnti) +{ + for (int i = 0; i < ra_count; ++i) { + const NR_RA_t *ra = &ra_base[i]; + if (ra->ra_state != nrRA_gNB_IDLE && ra->rnti == rnti) + return ra; + } + return NULL; +} + +bool nr_mac_get_new_rnti(NR_UEs_t *UEs, const NR_RA_t *ra_base, int ra_count, rnti_t *rnti) +{ + int loop = 0; + bool exist_connected_ue, exist_in_pending_ra_ue; + do { + *rnti = (taus() % 0xffef) + 1; + exist_connected_ue = find_nr_UE(UEs, *rnti) != NULL; + exist_in_pending_ra_ue = find_nr_RA_rnti(ra_base, ra_count, *rnti) != NULL; + loop++; + } while (loop < 100 && (exist_connected_ue || exist_in_pending_ra_ue)); + return loop < 100; // nothing found: loop count 100 +} diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h index 4ee100f812..a75b9c0f60 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h +++ b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h @@ -477,4 +477,6 @@ void process_addmod_bearers_cellGroupConfig(NR_UE_sched_ctrl_t *sched_ctrl, bool nr_mac_add_lcid(NR_UE_sched_ctrl_t *sched_ctrl, const nr_lc_config_t *c); bool nr_mac_remove_lcid(NR_UE_sched_ctrl_t *sched_ctrl, long lcid); +bool nr_mac_get_new_rnti(NR_UEs_t *UEs, const NR_RA_t *ra_base, int ra_count, rnti_t *rnti); + #endif /*__LAYER2_NR_MAC_PROTO_H__*/ From a927db9ef68802fe1a6a316f876ff5501b7fd966 Mon Sep 17 00:00:00 2001 From: rmagueta Date: Mon, 13 May 2024 17:25:14 +0100 Subject: [PATCH 07/41] Add c-rnti to UE context setup response No logic changes in this commit; to be used for the handover case later. Co-authored-by: rmagueta --- openair2/COMMON/f1ap_messages_types.h | 1 + openair2/F1AP/f1ap_cu_ue_context_management.c | 6 ++++++ openair2/F1AP/f1ap_du_ue_context_management.c | 7 ++----- openair2/LAYER2/NR_MAC_gNB/mac_rrc_ul_f1ap.c | 4 ++++ 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/openair2/COMMON/f1ap_messages_types.h b/openair2/COMMON/f1ap_messages_types.h index 569f3857c0..36c3ad4f76 100644 --- a/openair2/COMMON/f1ap_messages_types.h +++ b/openair2/COMMON/f1ap_messages_types.h @@ -485,6 +485,7 @@ typedef struct f1ap_ue_context_setup_s { //uint8_t *du_to_cu_rrc_information; du_to_cu_rrc_information_t *du_to_cu_rrc_information; uint32_t du_to_cu_rrc_information_length; + uint16_t *crnti; f1ap_drb_to_be_setup_t *drbs_to_be_setup; uint8_t drbs_to_be_setup_length; f1ap_drb_to_be_setup_t *drbs_to_be_modified; diff --git a/openair2/F1AP/f1ap_cu_ue_context_management.c b/openair2/F1AP/f1ap_cu_ue_context_management.c index 924c9ecfca..f72ac3c588 100644 --- a/openair2/F1AP/f1ap_cu_ue_context_management.c +++ b/openair2/F1AP/f1ap_cu_ue_context_management.c @@ -622,6 +622,12 @@ int CU_handle_UE_CONTEXT_SETUP_RESPONSE(instance_t instance, sctp_assoc_t assoc_ F1AP_ProtocolIE_ID_id_gNB_DU_UE_F1AP_ID, true); f1ap_ue_context_setup_resp->gNB_DU_ue_id = ie->value.choice.GNB_DU_UE_F1AP_ID; LOG_D(F1AP, "f1ap_ue_context_setup_resp->gNB_DU_ue_id is: %d \n", f1ap_ue_context_setup_resp->gNB_DU_ue_id); + F1AP_FIND_PROTOCOLIE_BY_ID(F1AP_UEContextSetupResponseIEs_t, ie, container, F1AP_ProtocolIE_ID_id_C_RNTI, false); + if (ie) { + f1ap_ue_context_setup_resp->crnti = calloc(1, sizeof(uint16_t)); + *f1ap_ue_context_setup_resp->crnti = ie->value.choice.C_RNTI; + } + // DUtoCURRCInformation F1AP_FIND_PROTOCOLIE_BY_ID(F1AP_UEContextSetupResponseIEs_t, ie, container, F1AP_ProtocolIE_ID_id_DUtoCURRCInformation, true); diff --git a/openair2/F1AP/f1ap_du_ue_context_management.c b/openair2/F1AP/f1ap_du_ue_context_management.c index 18a04962ad..d92da99d06 100644 --- a/openair2/F1AP/f1ap_du_ue_context_management.c +++ b/openair2/F1AP/f1ap_du_ue_context_management.c @@ -375,15 +375,12 @@ int DU_send_UE_CONTEXT_SETUP_RESPONSE(sctp_assoc_t assoc_id, f1ap_ue_context_set /* optional */ /* c4. C_RNTI */ - if (0) { + if (resp->crnti!=NULL) { asn1cSequenceAdd(out->protocolIEs.list, F1AP_UEContextSetupResponseIEs_t, ie4); ie4->id = F1AP_ProtocolIE_ID_id_C_RNTI; ie4->criticality = F1AP_Criticality_ignore; ie4->value.present = F1AP_UEContextSetupResponseIEs__value_PR_C_RNTI; - //C_RNTI_TO_BIT_STRING(rntiP, &ie->value.choice.C_RNTI); - ie4->value.choice.C_RNTI=0; - AssertFatal(false, "not implemented\n"); - LOG_E(F1AP,"RNTI to code!\n"); + ie4->value.choice.C_RNTI = *resp->crnti; } /* optional */ diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_rrc_ul_f1ap.c b/openair2/LAYER2/NR_MAC_gNB/mac_rrc_ul_f1ap.c index 2af81d0cfd..6d8f7a7115 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_rrc_ul_f1ap.c +++ b/openair2/LAYER2/NR_MAC_gNB/mac_rrc_ul_f1ap.c @@ -112,6 +112,10 @@ static void ue_context_setup_response_f1ap(const f1ap_ue_context_setup_t *req, c for (int i = 0; i < f1ap_msg->drbs_to_be_setup_length; ++i) f1ap_msg->drbs_to_be_setup[i] = resp->drbs_to_be_setup[i]; } + if (resp->crnti) { + f1ap_msg->crnti = calloc(1, sizeof(uint16_t)); + *f1ap_msg->crnti = *resp->crnti; + } f1ap_msg->du_to_cu_rrc_information = malloc(sizeof(*resp->du_to_cu_rrc_information)); AssertFatal(f1ap_msg->du_to_cu_rrc_information != NULL, "out of memory\n"); From c4a9bd1718c376172b10941b95c259eca00e247e Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 11 Jun 2024 06:41:01 +0200 Subject: [PATCH 08/41] Refactor reading of NR cap from capability list The next commit will modify the UE context setup request procedure to (possibly) receive UE capabilities through Handover Preparation Information, which extracts the UE capabilities for the UE to be setup. To avoid code duplication, extract the code that will be needed in UE capability extraction from Handover Preparation Information. --- .../LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c | 50 +++++++++++-------- 1 file changed, 29 insertions(+), 21 deletions(-) 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 a69ab2732e..a6193580de 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c +++ b/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c @@ -375,6 +375,33 @@ static int handle_ue_context_drbs_release(NR_UE_info_t *UE, return drbs_len; } +static NR_UE_NR_Capability_t *get_nr_cap(const NR_UE_CapabilityRAT_ContainerList_t *clist) +{ + for (int i = 0; i < clist->list.count; i++) { + const NR_UE_CapabilityRAT_Container_t *c = clist->list.array[i]; + if (c->rat_Type != NR_RAT_Type_nr) { + LOG_W(NR_MAC, "ignoring capability of type %ld\n", c->rat_Type); + continue; + } + + NR_UE_NR_Capability_t *cap = NULL; + asn_dec_rval_t dec_rval = uper_decode(NULL, + &asn_DEF_NR_UE_NR_Capability, + (void **)&cap, + c->ue_CapabilityRAT_Container.buf, + c->ue_CapabilityRAT_Container.size, + 0, + 0); + if (dec_rval.code != RC_OK) { + LOG_W(NR_MAC, "cannot decode NR UE capability, ignoring\n"); + ASN_STRUCT_FREE(asn_DEF_NR_UE_NR_Capability, cap); + continue; + } + return cap; + } + return NULL; +} + static NR_UE_NR_Capability_t *get_ue_nr_cap(int rnti, uint8_t *buf, uint32_t len) { if (buf == NULL || len == 0) @@ -387,27 +414,8 @@ static NR_UE_NR_Capability_t *get_ue_nr_cap(int rnti, uint8_t *buf, uint32_t len return NULL; } - NR_UE_NR_Capability_t *cap = NULL; - for (int i = 0; i < clist->list.count; i++) { - const NR_UE_CapabilityRAT_Container_t *c = clist->list.array[i]; - if (cap != NULL || c->rat_Type != NR_RAT_Type_nr) { - LOG_W(NR_MAC, "UE RNTI %04x: ignoring capability of type %ld\n", rnti, c->rat_Type); - continue; - } - - asn_dec_rval_t dec_rval = uper_decode(NULL, - &asn_DEF_NR_UE_NR_Capability, - (void **)&cap, - c->ue_CapabilityRAT_Container.buf, - c->ue_CapabilityRAT_Container.size, - 0, - 0); - if (dec_rval.code != RC_OK) { - LOG_W(NR_MAC, "cannot decode NR UE capabilities of UE RNTI %04x, ignoring NR capability\n", rnti); - cap = NULL; - continue; - } - } + NR_UE_NR_Capability_t *cap = get_nr_cap(clist); + ASN_STRUCT_FREE(asn_DEF_NR_UE_CapabilityRAT_ContainerList, clist); return cap; } From e7b56b3d088e867d46f9b8e14f2282220167fe3a Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 4 Jun 2024 14:27:44 +0200 Subject: [PATCH 09/41] UE context setup req: gNB_DU_ue_id optional, use 0xffffffff to signal --- openair2/F1AP/f1ap_du_ue_context_management.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/openair2/F1AP/f1ap_du_ue_context_management.c b/openair2/F1AP/f1ap_du_ue_context_management.c index d92da99d06..98de177af1 100644 --- a/openair2/F1AP/f1ap_du_ue_context_management.c +++ b/openair2/F1AP/f1ap_du_ue_context_management.c @@ -133,8 +133,13 @@ int DU_handle_UE_CONTEXT_SETUP_REQUEST(instance_t instance, sctp_assoc_t assoc_i /* GNB_DU_UE_F1AP_ID */ F1AP_UEContextSetupRequestIEs_t *ieDU_UE; F1AP_FIND_PROTOCOLIE_BY_ID(F1AP_UEContextSetupRequestIEs_t, ieDU_UE, container, - F1AP_ProtocolIE_ID_id_gNB_DU_UE_F1AP_ID, true); - f1ap_ue_context_setup_req->gNB_DU_ue_id = ieDU_UE->value.choice.GNB_DU_UE_F1AP_ID; + F1AP_ProtocolIE_ID_id_gNB_DU_UE_F1AP_ID, false); + // this should be optional. At present, the DU uses the RNTI, and hence, this + // value cannot exceed 0xffff (or something below, whatever is a valid + // C-RNTI). So we use a value > 0xffff to mark optionality + f1ap_ue_context_setup_req->gNB_DU_ue_id = -1; // 0xffffffff + if (ieDU_UE != NULL) + f1ap_ue_context_setup_req->gNB_DU_ue_id = ieDU_UE->value.choice.GNB_DU_UE_F1AP_ID; /* SpCell_ID */ F1AP_UEContextSetupRequestIEs_t *ieNet; From 0b2757b36acdb68d120c81eaa20213ed94279dc8 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Fri, 24 May 2024 20:01:53 +0200 Subject: [PATCH 10/41] Implement handler for F1 UE context Setup in HO case This commit implements adding a new UE on the target DU in case of handover. We identify this case by the fact that the gNB-CU did not send a gNB-DU UE ID (which is optional in F1AP, but not in our implementation), such that we know there is no corresponding UE, which therefore has to be created. If it happens that there is a handover preparation information, we read the UE capabilities from this information, and subsequently trigger the creation of a new UE. Another change is that for all bearers that we are asked to setup, we add reestablishRLC so that the UE does a reestablishment during the reconfiguration. As we have to remove the reconfigurationWithSync and reestablishRLC IE after application of the CellGroupConfig (because we might reuse the CellGroupConfig later), free these IEs. As there are now multiple fields to free, introduce a separate nr_mac_clean_cellgroup() function that handles cleaning of the CellGroupConfig. Co-authored-by: batuhanduyuler Co-authored-by: rmagueta --- .../NR_MAC_gNB/gNB_scheduler_primitives.c | 27 +++-- .../LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c | 101 ++++++++++++++++-- 2 files changed, 114 insertions(+), 14 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index b091cf69ee..0b09bb78a9 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -2950,6 +2950,25 @@ void nr_csirs_scheduling(int Mod_idP, frame_t frame, sub_frame_t slot, int n_slo } } +static void nr_mac_clean_cellgroup(NR_CellGroupConfig_t *cell_group) +{ + /* remove a reconfigurationWithSync, we don't need it anymore */ + if (cell_group->spCellConfig->reconfigurationWithSync != NULL) { + ASN_STRUCT_FREE(asn_DEF_NR_ReconfigurationWithSync, cell_group->spCellConfig->reconfigurationWithSync); + cell_group->spCellConfig->reconfigurationWithSync = NULL; + } + /* remove the rlc_BearerToReleaseList, we don't need it anymore */ + if (cell_group->rlc_BearerToReleaseList != NULL) { + struct NR_CellGroupConfig__rlc_BearerToReleaseList *l = cell_group->rlc_BearerToReleaseList; + asn_sequence_del(&l->list, l->list.count, /* free */ 1); + free(cell_group->rlc_BearerToReleaseList); + cell_group->rlc_BearerToReleaseList = NULL; + } + /* remove reestablishRLC, we don't need it anymore */ + for (int i = 0; i < cell_group->rlc_BearerToAddModList->list.count; ++i) + free_and_zero(cell_group->rlc_BearerToAddModList->list.array[i]->reestablishRLC); +} + static void nr_mac_apply_cellgroup(gNB_MAC_INST *mac, NR_UE_info_t *UE, frame_t frame, sub_frame_t slot) { LOG_D(NR_MAC, "%4d.%2d RNTI %04x: UE inactivity timer expired\n", frame, slot, UE->rnti); @@ -2965,13 +2984,7 @@ static void nr_mac_apply_cellgroup(gNB_MAC_INST *mac, NR_UE_info_t *UE, frame_t if (LOG_DEBUGFLAG(DEBUG_ASN1)) xer_fprint(stdout, &asn_DEF_NR_CellGroupConfig, (const void *)UE->CellGroup); - /* remove the rlc_BearerToReleaseList, we don't need it anymore */ - if (UE->CellGroup->rlc_BearerToReleaseList != NULL) { - struct NR_CellGroupConfig__rlc_BearerToReleaseList *l = UE->CellGroup->rlc_BearerToReleaseList; - asn_sequence_del(&l->list, l->list.count, /* free */1); - free(UE->CellGroup->rlc_BearerToReleaseList); - UE->CellGroup->rlc_BearerToReleaseList = NULL; - } + nr_mac_clean_cellgroup(UE->CellGroup); } NR_ServingCellConfigCommon_t *scc = mac->common_channels[0].ServingCellConfigCommon; 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 a6193580de..4089b224d1 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c +++ b/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c @@ -26,6 +26,7 @@ #include "openair2/F1AP/f1ap_common.h" #include "openair2/LAYER2/nr_rlc/nr_rlc_oai_api.h" #include "F1AP_CauseRadioNetwork.h" +#include "NR_HandoverPreparationInformation.h" #include "openair3/ocp-gtpu/gtp_itf.h" #include "openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.h" #include "lib/f1ap_interface_management.h" @@ -419,6 +420,35 @@ static NR_UE_NR_Capability_t *get_ue_nr_cap(int rnti, uint8_t *buf, uint32_t len return cap; } +/* \brief return UE capabilties from HandoverPreparationInformation. + * + * The HandoverPreparationInformation contains more, but for the moment, let's + * keep it simple and only handle that. The function asserts if other IEs are + * present. */ +static NR_UE_NR_Capability_t *get_ue_nr_cap_from_ho_prep_info(uint8_t *buf, uint32_t len) +{ + if (buf == NULL || len == 0) + return NULL; + NR_HandoverPreparationInformation_t *hpi = NULL; + asn_dec_rval_t dec_rval = uper_decode_complete(NULL, &asn_DEF_NR_HandoverPreparationInformation, (void **)&hpi, buf, len); + if (dec_rval.code != RC_OK) { + LOG_W(NR_MAC, "cannot decode HandoverPreparationInformation, ignoring capabilities\n"); + return NULL; + } + NR_UE_NR_Capability_t *cap = NULL; + if (hpi->criticalExtensions.present != NR_HandoverPreparationInformation__criticalExtensions_PR_c1 + || hpi->criticalExtensions.choice.c1 == NULL + || hpi->criticalExtensions.choice.c1->present + != NR_HandoverPreparationInformation__criticalExtensions__c1_PR_handoverPreparationInformation + || hpi->criticalExtensions.choice.c1->choice.handoverPreparationInformation == NULL) { + } else { + const NR_HandoverPreparationInformation_IEs_t *hpi_ie = hpi->criticalExtensions.choice.c1->choice.handoverPreparationInformation; + cap = get_nr_cap(&hpi_ie->ue_CapabilityRAT_List); + } + ASN_STRUCT_FREE(asn_DEF_NR_HandoverPreparationInformation, hpi); + return cap; +} + NR_CellGroupConfig_t *clone_CellGroupConfig(const NR_CellGroupConfig_t *orig) { uint8_t buf[16636]; @@ -431,6 +461,40 @@ NR_CellGroupConfig_t *clone_CellGroupConfig(const NR_CellGroupConfig_t *orig) return cloned; } +static NR_UE_info_t *create_new_UE(gNB_MAC_INST *mac, uint32_t cu_id) +{ + int CC_id = 0; + const NR_COMMON_channels_t *cc = &mac->common_channels[CC_id]; + rnti_t rnti; + bool found = nr_mac_get_new_rnti(&mac->UE_info, cc->ra, sizeofArray(cc->ra), &rnti); + if (!found) + return NULL; + + NR_UE_info_t* UE = add_new_nr_ue(mac, rnti, NULL); + if (!UE) + return NULL; + + f1_ue_data_t new_ue_data = {.secondary_ue = cu_id}; + du_add_f1_ue_data(rnti, &new_ue_data); + + const NR_ServingCellConfigCommon_t *scc = mac->common_channels[CC_id].ServingCellConfigCommon; + const NR_ServingCellConfig_t *sccd = mac->common_channels[CC_id].pre_ServingCellConfig; + NR_CellGroupConfig_t *cellGroupConfig = get_initial_cellGroupConfig(UE->uid, scc, sccd, &mac->radio_config); + cellGroupConfig->spCellConfig->reconfigurationWithSync = get_reconfiguration_with_sync(UE->rnti, UE->uid, scc); + // note: we don't pass the cellGroupConfig to add_new_nr_ue() because we need + // the uid to create the CellGroupConfig (which is in the UE context created + // by add_new_nr_ue(); it's a kind of chicken-and-egg problem), so below we + // complete the UE context with the information that add_new_nr_ue() would + // have added + UE->Msg4_MsgB_ACKed = true; + UE->CellGroup = cellGroupConfig; + + nr_rlc_activate_srb0(UE->rnti, UE, NULL); + nr_mac_prepare_ra_ue(mac, rnti, UE->CellGroup); + /* SRB1 is added to RLC and MAC in the handler later */ + return UE; +} + void ue_context_setup_request(const f1ap_ue_context_setup_t *req) { gNB_MAC_INST *mac = RC.nrmac[0]; @@ -440,19 +504,34 @@ void ue_context_setup_request(const f1ap_ue_context_setup_t *req) .gNB_DU_ue_id = req->gNB_DU_ue_id, }; + /* gNB-DU UE ID is optional. As of now, the F1AP module fills -1 (on uint, so + * 0xffffffff), but the DU uses the RNTI, so check if it's a legal RNTI in + * which case we consider; if not, assume no DU UE ID given */ + bool ue_id_provided = resp.gNB_DU_ue_id > 0 && resp.gNB_DU_ue_id < 0xffff; + NR_UE_NR_Capability_t *ue_cap = NULL; if (req->cu_to_du_rrc_information != NULL) { - AssertFatal(req->cu_to_du_rrc_information->cG_ConfigInfo == NULL, "CG-ConfigInfo not handled\n"); - ue_cap = get_ue_nr_cap(req->gNB_DU_ue_id, - req->cu_to_du_rrc_information->uE_CapabilityRAT_ContainerList, - req->cu_to_du_rrc_information->uE_CapabilityRAT_ContainerList_length); - AssertFatal(req->cu_to_du_rrc_information->measConfig == NULL, "MeasConfig not handled\n"); + const cu_to_du_rrc_information_t *cu2du = req->cu_to_du_rrc_information; + AssertFatal(cu2du->cG_ConfigInfo == NULL, "CG-ConfigInfo not handled\n"); + if (cu2du->handoverPreparationInfo != NULL) { + ue_cap = get_ue_nr_cap_from_ho_prep_info(cu2du->handoverPreparationInfo, cu2du->handoverPreparationInfo_length); + } else if (cu2du->uE_CapabilityRAT_ContainerList != NULL) { + ue_cap = get_ue_nr_cap(req->gNB_DU_ue_id, cu2du->uE_CapabilityRAT_ContainerList, cu2du->uE_CapabilityRAT_ContainerList_length); + } + AssertFatal(cu2du->measConfig == NULL, "MeasConfig not handled\n"); } NR_SCHED_LOCK(&mac->sched_lock); - NR_UE_info_t *UE = find_nr_UE(&RC.nrmac[0]->UE_info, req->gNB_DU_ue_id); - AssertFatal(UE, "did not find UE with RNTI %04x, but UE Context Setup Failed not implemented\n", req->gNB_DU_ue_id); + NR_UE_info_t *UE = NULL; + if (!ue_id_provided) { + UE = create_new_UE(mac, req->gNB_CU_ue_id); + resp.gNB_DU_ue_id = UE->rnti; + resp.crnti = &UE->rnti; + } else { + UE = find_nr_UE(&mac->UE_info, req->gNB_DU_ue_id); + } + AssertFatal(UE, "no UE found or could not be created, but UE Context Setup Failed not implemented\n"); NR_CellGroupConfig_t *new_CellGroup = clone_CellGroupConfig(UE->CellGroup); @@ -484,6 +563,14 @@ void ue_context_setup_request(const f1ap_ue_context_setup_t *req) update_cellGroupConfig(new_CellGroup, UE->uid, UE->capability, &mac->radio_config, scc); } + if (!ue_id_provided) { + /* new UE: tell the UE to reestablish RLC */ + struct NR_CellGroupConfig__rlc_BearerToAddModList *addmod = new_CellGroup->rlc_BearerToAddModList; + for (int i = 0; i < addmod->list.count; ++i) { + asn1cCallocOne(addmod->list.array[i]->reestablishRLC, NR_RLC_BearerConfig__reestablishRLC_true); + } + } + resp.du_to_cu_rrc_information = calloc(1, sizeof(du_to_cu_rrc_information_t)); AssertFatal(resp.du_to_cu_rrc_information != NULL, "out of memory\n"); resp.du_to_cu_rrc_information->cellGroupConfig = calloc(1,1024); From dadd05e0eb023e68a6ed74ec7dd4de5a10d0b6cf Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Fri, 2 Aug 2024 16:48:22 +0200 Subject: [PATCH 11/41] Skip adding of second LCID1 Our initial CellGroupConfig always contains LCID1, because in normal connection setup, the DU has to automatically set it up anyway. In handover/UE Context Setup, the CU requests to set up SRB1 (which we assume to be LCID1), but we reuse the initial CellGroupConfig. To not have SRB1 added twice, skip in this case. --- openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) 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 4089b224d1..e7ff5c4574 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c +++ b/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c @@ -238,8 +238,16 @@ static int handle_ue_context_srbs_setup(NR_UE_info_t *UE, (*resp_srbs)[i].srb_id = srb->srb_id; (*resp_srbs)[i].lcid = c.lcid; - int ret = ASN_SEQUENCE_ADD(&cellGroupConfig->rlc_BearerToAddModList->list, rlc_BearerConfig); - DevAssert(ret == 0); + if (rlc_BearerConfig->logicalChannelIdentity == 1) { + // CU asks to add SRB1: when creating a cellGroupConfig, we always add it + // (see get_initial_cellGroupConfig()) + const struct NR_CellGroupConfig__rlc_BearerToAddModList *addmod = cellGroupConfig->rlc_BearerToAddModList; + DevAssert(addmod->list.count >= 1 && addmod->list.array[0]->logicalChannelIdentity == 1); + ASN_STRUCT_FREE(asn_DEF_NR_RLC_BearerConfig, rlc_BearerConfig); + } else { + int ret = ASN_SEQUENCE_ADD(&cellGroupConfig->rlc_BearerToAddModList->list, rlc_BearerConfig); + DevAssert(ret == 0); + } } return srbs_len; } From 1783932e37fd7961210c7e513a762bba21ecc002 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 7 Aug 2024 09:44:42 +0200 Subject: [PATCH 12/41] Msg2: Apply CellGroupConfig if necessary After RA, in the case of HO, we need to make sure to use the right radio configuration (MCS table, etc). This commit is important to make HO actually work. --- 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 061ad1ba27..e43123b55c 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c @@ -1678,7 +1678,8 @@ static void nr_generate_Msg2(module_id_t module_idP, NR_UE_info_t *UE = find_nr_UE(&RC.nrmac[module_idP]->UE_info, ra->rnti); if (UE) { int delay = nr_mac_get_reconfig_delay_slots(ra->DL_BWP.scs); - nr_mac_interrupt_ue_transmission(RC.nrmac[module_idP], UE, FOLLOW_INSYNC, delay); + interrupt_followup_action_t action = UE->reconfigCellGroup ? FOLLOW_INSYNC_RECONFIG : FOLLOW_INSYNC; + nr_mac_interrupt_ue_transmission(RC.nrmac[module_idP], UE, action, delay); } } From 5378c4d8d960ab39cf42211a672e285b8ad1ab32 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 4 Jun 2024 10:40:10 +0200 Subject: [PATCH 13/41] Refactor RRC Reconfiguration function Refactor the RRC reconfiguration function to reuse it also later for sending the reconfiguration including the handover trigger. --- openair2/RRC/NR/rrc_gNB.c | 101 +++++++++++++++++++++----------------- 1 file changed, 57 insertions(+), 44 deletions(-) diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index 5ab7c0ba12..9803991db5 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -481,6 +481,60 @@ static void rrc_gNB_process_RRCSetupComplete(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE rrc_gNB_send_NGAP_NAS_FIRST_REQ(rrc, UE, rrcSetupComplete); } +static int rrc_gNB_encode_RRCReconfiguration(gNB_RRC_INST *rrc, + gNB_RRC_UE_t *UE, + uint8_t xid, + struct NR_RRCReconfiguration_v1530_IEs__dedicatedNAS_MessageList *nas_messages, + uint8_t *buf, + int max_len, + bool reestablish) +{ + NR_CellGroupConfig_t *cellGroupConfig = UE->masterCellGroup; + nr_rrc_du_container_t *du = get_du_for_ue(rrc, UE->rrc_ue_id); + DevAssert(du != NULL); + f1ap_served_cell_info_t *cell_info = &du->setup_req->cell[0].info; + NR_MeasConfig_t *measconfig = NULL; + if (du->mtc != NULL) { + int scs = get_ssb_scs(cell_info); + int band = get_dl_band(cell_info); + const NR_MeasTimingList_t *mtlist = du->mtc->criticalExtensions.choice.c1->choice.measTimingConf->measTiming; + const NR_MeasTiming_t *mt = mtlist->list.array[0]; + const neighbour_cell_configuration_t *neighbour_config = get_neighbour_config(cell_info->nr_cellid); + seq_arr_t *neighbour_cells = NULL; + if (neighbour_config) + neighbour_cells = neighbour_config->neighbour_cells; + + measconfig = get_MeasConfig(mt, band, scs, &rrc->measurementConfiguration, neighbour_cells); + } + + if (UE->measConfig) + free_MeasConfig(UE->measConfig); + + UE->measConfig = measconfig; + + NR_SRB_ToAddModList_t *SRBs = createSRBlist(UE, reestablish); + NR_DRB_ToAddModList_t *DRBs = createDRBlist(UE, reestablish); + + int size = do_RRCReconfiguration(UE, + buf, + max_len, + xid, + SRBs, + DRBs, + UE->DRB_ReleaseList, + NULL, + measconfig, + nas_messages, + cellGroupConfig); + LOG_DUMPMSG(NR_RRC, DEBUG_RRC, (char *)buf, size, "[MSG] RRC Reconfiguration\n"); + freeSRBlist(SRBs); + freeDRBlist(DRBs); + ASN_STRUCT_FREE(asn_DEF_NR_DRB_ToReleaseList, UE->DRB_ReleaseList); + UE->DRB_ReleaseList = NULL; + + return size; +} + static void rrc_gNB_generate_dedicatedRRCReconfiguration(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue_p) { uint8_t xid = rrc_gNB_get_next_transaction_identifier(rrc->module_id); @@ -517,51 +571,10 @@ static void rrc_gNB_generate_dedicatedRRCReconfiguration(gNB_RRC_INST *rrc, gNB_ for (int i = 0; i < ue_p->nb_of_pdusessions; i++) clear_nas_pdu(&ue_p->pduSession[i].param.nas_pdu); - NR_CellGroupConfig_t *cellGroupConfig = ue_p->masterCellGroup; - - const nr_rrc_du_container_t *du = get_du_for_ue(rrc, ue_p->rrc_ue_id); - DevAssert(du != NULL); - f1ap_served_cell_info_t *cell_info = &du->setup_req->cell[0].info; - NR_MeasConfig_t *measconfig = NULL; - - if (du->mtc != NULL) { - int scs = get_ssb_scs(cell_info); - int band = get_dl_band(cell_info); - const NR_MeasTimingList_t *mtlist = du->mtc->criticalExtensions.choice.c1->choice.measTimingConf->measTiming; - const NR_MeasTiming_t *mt = mtlist->list.array[0]; - const neighbour_cell_configuration_t *neighbour_config = get_neighbour_config(cell_info->nr_cellid); - seq_arr_t *neighbour_cells = NULL; - if (neighbour_config) - neighbour_cells = neighbour_config->neighbour_cells; - - measconfig = get_MeasConfig(mt, band, scs, &rrc->measurementConfiguration, neighbour_cells); - } - - if (ue_p->measConfig) - free_MeasConfig(ue_p->measConfig); - - ue_p->measConfig = measconfig; - uint8_t buffer[NR_RRC_BUF_SIZE] = {0}; - NR_SRB_ToAddModList_t *SRBs = createSRBlist(ue_p, false); - NR_DRB_ToAddModList_t *DRBs = createDRBlist(ue_p, false); - - int size = do_RRCReconfiguration(ue_p, - buffer, - NR_RRC_BUF_SIZE, - xid, - SRBs, - DRBs, - ue_p->DRB_ReleaseList, - NULL, - measconfig, - dedicatedNAS_MessageList, - cellGroupConfig); - LOG_DUMPMSG(NR_RRC, DEBUG_RRC, (char *)buffer, size, "[MSG] RRC Reconfiguration\n"); - freeSRBlist(SRBs); - freeDRBlist(DRBs); - ASN_STRUCT_FREE(asn_DEF_NR_DRB_ToReleaseList, ue_p->DRB_ReleaseList); - ue_p->DRB_ReleaseList = NULL; + // TODO refactor dedicatedNAS_MessageList + int size = rrc_gNB_encode_RRCReconfiguration(rrc, ue_p, xid, dedicatedNAS_MessageList, buffer, sizeof(buffer), false); + DevAssert(size > 0 && size <= sizeof(buffer)); LOG_I(NR_RRC, "UE %d: Generate RRCReconfiguration (bytes %d, xid %d)\n", ue_p->rrc_ue_id, size, xid); nr_rrc_transfer_protected_rrc_message(rrc, ue_p, DCCH, buffer, size); From 4a5bf53caad55fd0255e02c396061f8533ce7675 Mon Sep 17 00:00:00 2001 From: rmagueta Date: Thu, 25 Jul 2024 13:38:51 +0200 Subject: [PATCH 14/41] UE: Improve synchronization for reconfigurationWithSync Improve synchronization to also handle handover between gNB-DUs with differing carrier freqs. --- executables/nr-ue.c | 19 +++++++++++++++++-- openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c | 2 +- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/executables/nr-ue.c b/executables/nr-ue.c index 01f18dc38d..a06c13a59a 100644 --- a/executables/nr-ue.c +++ b/executables/nr-ue.c @@ -38,6 +38,7 @@ #include "LAYER2/nr_rlc/nr_rlc_oai_api.h" #include "RRC/NR/MESSAGES/asn1_msg.h" #include "openair1/PHY/TOOLS/phy_scope_interface.h" +#include "PHY/MODULATION/nr_modulation.h" /* * NR SLOT PROCESSING SEQUENCE @@ -604,9 +605,23 @@ static int UE_dl_preprocessing(PHY_VARS_NR_UE *UE, const UE_nr_rxtx_proc_t *proc // Start synchronization with a target gNB if (UE->synch_request.received_synch_request == 1) { + fapi_nr_synch_request_t *synch_req = &UE->synch_request.synch_req; UE->is_synchronized = 0; - UE->UE_scan_carrier = UE->synch_request.synch_req.ssb_bw_scan; - UE->target_Nid_cell = UE->synch_request.synch_req.target_Nid_cell; + UE->UE_scan_carrier = synch_req->ssb_bw_scan; + UE->target_Nid_cell = synch_req->target_Nid_cell; + + uint64_t dl_bw = (12 * fp->N_RB_DL) * (15000 << fp->numerology_index); + uint64_t dl_CarrierFreq = (dl_bw >> 1) + (uint64_t)UE->nrUE_config.carrier_config.dl_frequency * 1000; + uint64_t ul_bw = (12 * fp->N_RB_UL) * (15000 << fp->numerology_index); + uint64_t ul_CarrierFreq = (ul_bw >> 1) + (uint64_t)UE->nrUE_config.carrier_config.uplink_frequency * 1000; + if (dl_CarrierFreq != fp->dl_CarrierFreq || ul_CarrierFreq != fp->ul_CarrierFreq) { + fp->dl_CarrierFreq = dl_CarrierFreq; + fp->ul_CarrierFreq = ul_CarrierFreq; + nr_rf_card_config_freq(&openair0_cfg[UE->rf_map.card], ul_CarrierFreq, dl_CarrierFreq, 0); + UE->rfdevice.trx_set_freq_func(&UE->rfdevice, &openair0_cfg[0]); + init_symbol_rotation(fp); + } + clean_UE_harq(UE); UE->synch_request.received_synch_request = 0; } diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c index 9003cc42d2..1eb79ebda5 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c @@ -2240,7 +2240,7 @@ static int get_nr_prach_info_from_ssb_index(prach_association_pattern_t *prach_a ssb_info_p->mapped_ro[n_mapped_ro]->frame, ssb_info_p->mapped_ro[n_mapped_ro]->slot, prach_assoc_pattern->nb_of_frame); - if ((slot == ssb_info_p->mapped_ro[n_mapped_ro]->slot) && + if ((slot == ssb_info_p->mapped_ro[n_mapped_ro]->slot) && prach_assoc_pattern->prach_conf_period_list[0].nb_of_frame != 0 && (ssb_info_p->mapped_ro[n_mapped_ro]->frame == (frame % prach_assoc_pattern->nb_of_frame))) { uint8_t prach_config_period_nb = ssb_info_p->mapped_ro[n_mapped_ro]->frame / prach_assoc_pattern->prach_conf_period_list[0].nb_of_frame; uint8_t frame_nb_in_prach_config_period = ssb_info_p->mapped_ro[n_mapped_ro]->frame % prach_assoc_pattern->prach_conf_period_list[0].nb_of_frame; From cddffe6f3055a80d8e0d619cafcd7095d4847e2d Mon Sep 17 00:00:00 2001 From: Roberto Magueta Date: Tue, 30 Apr 2024 15:13:36 +0100 Subject: [PATCH 15/41] Add config files (TDD/FDD) for multiple DUs scenario Co-authored-by: rmagueta --- .../GENERIC-NR-5GC/CONF/gnb-cu.sa.f1.conf | 74 ++++++ .../gnb-du.sa.band66.25prb.rfsim.pci0.conf | 215 ++++++++++++++++++ .../gnb-du.sa.band66.25prb.rfsim.pci1.conf | 215 ++++++++++++++++++ .../gnb-du.sa.band78.106prb.rfsim.pci0.conf | 212 +++++++++++++++++ .../gnb-du.sa.band78.106prb.rfsim.pci1.conf | 212 +++++++++++++++++ 5 files changed, 928 insertions(+) create mode 100644 targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-cu.sa.f1.conf create mode 100644 targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band66.25prb.rfsim.pci0.conf create mode 100644 targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band66.25prb.rfsim.pci1.conf create mode 100644 targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band78.106prb.rfsim.pci0.conf create mode 100644 targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band78.106prb.rfsim.pci1.conf diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-cu.sa.f1.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-cu.sa.f1.conf new file mode 100644 index 0000000000..08b72bf6c6 --- /dev/null +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-cu.sa.f1.conf @@ -0,0 +1,74 @@ +Active_gNBs = ( "cu-rfsim"); +# Asn1_verbosity, choice in: none, info, annoying +Asn1_verbosity = "none"; +Num_Threads_PUSCH = 8; + +gNBs = +( + { + ////////// Identification parameters: + gNB_ID = 0xe00; + gNB_name = "cu-rfsim"; + + // Tracking area code, 0x0000 and 0xfffe are reserved values + tracking_area_code = 1; + plmn_list = ({ mcc = 001; mnc = 01; mnc_length = 2; snssaiList = ({ sst = 1; }) }); + + nr_cellid = 12345678L; + + tr_s_preference = "f1"; + + local_s_if_name = "lo"; + local_s_address = "127.0.0.3"; + remote_s_address = "0.0.0.0"; # multiple DUs + local_s_portc = 501; + local_s_portd = 2152; + remote_s_portc = 500; + remote_s_portd = 2152; + + + # ------- SCTP definitions + SCTP : + { + # Number of streams to use in input/output + SCTP_INSTREAMS = 2; + SCTP_OUTSTREAMS = 2; + }; + + + ////////// AMF parameters: + amf_ip_address = ({ ipv4 = "192.168.70.132"; }); + + NETWORK_INTERFACES : + { + GNB_IPV4_ADDRESS_FOR_NG_AMF = "192.168.70.129/24"; + GNB_IPV4_ADDRESS_FOR_NGU = "192.168.70.129/24"; + GNB_PORT_FOR_S1U = 2152; # Spec 2152 + }; + } +); + +security = { + # preferred ciphering algorithms + # the first one of the list that an UE supports in chosen + # valid values: nea0, nea1, nea2, nea3 + ciphering_algorithms = ( "nea0" ); + + # preferred integrity algorithms + # the first one of the list that an UE supports in chosen + # valid values: nia0, nia1, nia2, nia3 + integrity_algorithms = ( "nia2", "nia0" ); + + # setting 'drb_ciphering' to "no" disables ciphering for DRBs, no matter + # what 'ciphering_algorithms' configures; same thing for 'drb_integrity' + drb_ciphering = "yes"; + drb_integrity = "no"; +}; + +log_config : { + global_log_level = "info"; + pdcp_log_level = "info"; + rrc_log_level = "info"; + f1ap_log_level = "info"; + ngap_log_level = "info"; +}; diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band66.25prb.rfsim.pci0.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band66.25prb.rfsim.pci0.conf new file mode 100644 index 0000000000..abd624747d --- /dev/null +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band66.25prb.rfsim.pci0.conf @@ -0,0 +1,215 @@ +Active_gNBs = ( "du-rfsim"); +# Asn1_verbosity, choice in: none, info, annoying +Asn1_verbosity = "none"; + +gNBs = +( + { + ////////// Identification parameters: + gNB_ID = 0xe00; + gNB_DU_ID = 0xe00; + gNB_name = "du-rfsim"; + + // Tracking area code, 0x0000 and 0xfffe are reserved values + tracking_area_code = 1; + plmn_list = ({ mcc = 208; mnc = 95; mnc_length = 2; snssaiList = ({ sst = 1; }) }); + + nr_cellid = 12345678L; + + ////////// Physical parameters: + + min_rxtxtime = 4; + sib1_tda = 15; + + servingCellConfigCommon = ( + { + #spCellConfigCommon + + physCellId = 0; + +# downlinkConfigCommon + #frequencyInfoDL + # this is 3600 MHz + 43 PRBs@30kHz SCS (same as initial BWP) + absoluteFrequencySSB = 430610; + dl_frequencyBand = 66; + # this is 3600 MHz + dl_absoluteFrequencyPointA = 430000; + #scs-SpecificCarrierList + dl_offstToCarrier = 0; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + dl_subcarrierSpacing = 0; + dl_carrierBandwidth = 25; + #initialDownlinkBWP + #genericParameters + # this is RBstart=27,L=48 (275*(L-1))+RBstart + initialDLBWPlocationAndBandwidth = 6600; # 6366 12925 12956 28875 12952 +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + initialDLBWPsubcarrierSpacing = 0; + #pdcch-ConfigCommon + initialDLBWPcontrolResourceSetZero = 0; + initialDLBWPsearchSpaceZero = 0; + + #uplinkConfigCommon + #frequencyInfoUL + ul_frequencyBand = 66; + # this is 1750 MHz + ul_absoluteFrequencyPointA = 350000; + #scs-SpecificCarrierList + ul_offstToCarrier = 0; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + ul_subcarrierSpacing = 0; + ul_carrierBandwidth = 25; + pMax = 20; + #initialUplinkBWP + #genericParameters + initialULBWPlocationAndBandwidth = 6600; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + initialULBWPsubcarrierSpacing = 0; + #rach-ConfigCommon + #rach-ConfigGeneric + prach_ConfigurationIndex = 98; +#prach_msg1_FDM +#0 = one, 1=two, 2=four, 3=eight + prach_msg1_FDM = 0; + prach_msg1_FrequencyStart = 0; + zeroCorrelationZoneConfig = 13; + preambleReceivedTargetPower = -96; +#preamblTransMax (0...10) = (3,4,5,6,7,8,10,20,50,100,200) + preambleTransMax = 6; +#powerRampingStep +# 0=dB0,1=dB2,2=dB4,3=dB6 + powerRampingStep = 1; +#ra_ReponseWindow +#1,2,4,8,10,20,40,80 + ra_ResponseWindow = 4; +#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR +#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen + ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4; +#oneHalf (0..15) 4,8,12,16,...60,64 + ssb_perRACH_OccasionAndCB_PreamblesPerSSB = 15; +#ra_ContentionResolutionTimer +#(0..7) 8,16,24,32,40,48,56,64 + ra_ContentionResolutionTimer = 7; + rsrp_ThresholdSSB = 19; +#prach-RootSequenceIndex_PR +#1 = 839, 2 = 139 + prach_RootSequenceIndex_PR = 2; + prach_RootSequenceIndex = 1; + # SCS for msg1, can only be 15 for 30 kHz < 6 GHz, takes precendence over the one derived from prach-ConfigIndex + # + msg1_SubcarrierSpacing = 0, +# restrictedSetConfig +# 0=unrestricted, 1=restricted type A, 2=restricted type B + restrictedSetConfig = 0, + + msg3_DeltaPreamble = 1; + p0_NominalWithGrant =-90; + +# pucch-ConfigCommon setup : +# pucchGroupHopping +# 0 = neither, 1= group hopping, 2=sequence hopping + pucchGroupHopping = 0; + hoppingId = 40; + p0_nominal = -90; + + ssb_PositionsInBurst_Bitmap = 1; + +# ssb_periodicityServingCell +# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1 + ssb_periodicityServingCell = 2; + +# dmrs_TypeA_position +# 0 = pos2, 1 = pos3 + dmrs_TypeA_Position = 0; + +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + subcarrierSpacing = 0; + + + #tdd-UL-DL-ConfigurationCommon +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + referenceSubcarrierSpacing = 0; + # pattern1 + # dl_UL_TransmissionPeriodicity + # 0=ms0p5, 1=ms0p625, 2=ms1, 3=ms1p25, 4=ms2, 5=ms2p5, 6=ms5, 7=ms10 + dl_UL_TransmissionPeriodicity = 6; + nrofDownlinkSlots = 7; + nrofDownlinkSymbols = 6; + nrofUplinkSlots = 2; + nrofUplinkSymbols = 4; + + ssPBCH_BlockPower = -25; + } + + ); + + # ------- SCTP definitions + SCTP : + { + # Number of streams to use in input/output + SCTP_INSTREAMS = 2; + SCTP_OUTSTREAMS = 2; + }; + } +); + +MACRLCs = ({ + num_cc = 1; + tr_s_preference = "local_L1"; + tr_n_preference = "f1"; + local_n_if_name = "lo"; + local_n_address = "127.0.0.4"; + remote_n_address = "127.0.0.3"; + local_n_portc = 500; + local_n_portd = 2152; + remote_n_portc = 501; + remote_n_portd = 2152; + pusch_FailureThres = 1000; +}); + +L1s = ({ + num_cc = 1; + tr_n_preference = "local_mac"; + prach_dtx_threshold = 200; + pucch0_dtx_threshold = 150; + ofdm_offset_divisor = 8; #set this to UINT_MAX for offset 0 +}); + +RUs = ({ + local_rf = "yes" + nb_tx = 1 + nb_rx = 1 + att_tx = 0 + att_rx = 0; + bands = [66]; + max_pdschReferenceSignalPower = -27; + max_rxgain = 114; + eNB_instances = [0]; + clock_src = "internal"; +}); + +rfsimulator: { + serveraddr = "server"; + serverport = 4043; + options = (); #("saviq"); or/and "chanmod" + modelname = "AWGN"; + IQfile = "/tmp/rfsimulator.iqs" +} + +log_config: { + global_log_level = "info"; + hw_log_level = "info"; + nr_phy_log_level = "info"; + nr_mac_log_level = "info"; + rlc_log_level = "info"; + pdcp_log_level = "info"; + rrc_log_level = "info"; + f1ap_log_level = "info"; + ngap_log_level = "debug"; +}; diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band66.25prb.rfsim.pci1.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band66.25prb.rfsim.pci1.conf new file mode 100644 index 0000000000..8d2e46b44e --- /dev/null +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band66.25prb.rfsim.pci1.conf @@ -0,0 +1,215 @@ +Active_gNBs = ( "du-rfsim"); +# Asn1_verbosity, choice in: none, info, annoying +Asn1_verbosity = "none"; + +gNBs = +( + { + ////////// Identification parameters: + gNB_ID = 0xe00; + gNB_DU_ID = 0xe01; + gNB_name = "du-rfsim"; + + // Tracking area code, 0x0000 and 0xfffe are reserved values + tracking_area_code = 1; + plmn_list = ({ mcc = 208; mnc = 95; mnc_length = 2; snssaiList = ({ sst = 1; }) }); + + nr_cellid = 11111111L; + + ////////// Physical parameters: + + min_rxtxtime = 4; + sib1_tda = 15; + + servingCellConfigCommon = ( + { + #spCellConfigCommon + + physCellId = 1; + +# downlinkConfigCommon + #frequencyInfoDL + # this is 3600 MHz + 43 PRBs@30kHz SCS (same as initial BWP) + absoluteFrequencySSB = 435610; + dl_frequencyBand = 66; + # this is 3600 MHz + dl_absoluteFrequencyPointA = 435000; + #scs-SpecificCarrierList + dl_offstToCarrier = 0; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + dl_subcarrierSpacing = 0; + dl_carrierBandwidth = 25; + #initialDownlinkBWP + #genericParameters + # this is RBstart=27,L=48 (275*(L-1))+RBstart + initialDLBWPlocationAndBandwidth = 6600; # 6366 12925 12956 28875 12952 +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + initialDLBWPsubcarrierSpacing = 0; + #pdcch-ConfigCommon + initialDLBWPcontrolResourceSetZero = 0; + initialDLBWPsearchSpaceZero = 0; + + #uplinkConfigCommon + #frequencyInfoUL + ul_frequencyBand = 66; + # this is 1750 MHz + ul_absoluteFrequencyPointA = 355000; + #scs-SpecificCarrierList + ul_offstToCarrier = 0; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + ul_subcarrierSpacing = 0; + ul_carrierBandwidth = 25; + pMax = 20; + #initialUplinkBWP + #genericParameters + initialULBWPlocationAndBandwidth = 6600; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + initialULBWPsubcarrierSpacing = 0; + #rach-ConfigCommon + #rach-ConfigGeneric + prach_ConfigurationIndex = 98; +#prach_msg1_FDM +#0 = one, 1=two, 2=four, 3=eight + prach_msg1_FDM = 0; + prach_msg1_FrequencyStart = 0; + zeroCorrelationZoneConfig = 13; + preambleReceivedTargetPower = -96; +#preamblTransMax (0...10) = (3,4,5,6,7,8,10,20,50,100,200) + preambleTransMax = 6; +#powerRampingStep +# 0=dB0,1=dB2,2=dB4,3=dB6 + powerRampingStep = 1; +#ra_ReponseWindow +#1,2,4,8,10,20,40,80 + ra_ResponseWindow = 4; +#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR +#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen + ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4; +#oneHalf (0..15) 4,8,12,16,...60,64 + ssb_perRACH_OccasionAndCB_PreamblesPerSSB = 15; +#ra_ContentionResolutionTimer +#(0..7) 8,16,24,32,40,48,56,64 + ra_ContentionResolutionTimer = 7; + rsrp_ThresholdSSB = 19; +#prach-RootSequenceIndex_PR +#1 = 839, 2 = 139 + prach_RootSequenceIndex_PR = 2; + prach_RootSequenceIndex = 1; + # SCS for msg1, can only be 15 for 30 kHz < 6 GHz, takes precendence over the one derived from prach-ConfigIndex + # + msg1_SubcarrierSpacing = 0, +# restrictedSetConfig +# 0=unrestricted, 1=restricted type A, 2=restricted type B + restrictedSetConfig = 0, + + msg3_DeltaPreamble = 1; + p0_NominalWithGrant =-90; + +# pucch-ConfigCommon setup : +# pucchGroupHopping +# 0 = neither, 1= group hopping, 2=sequence hopping + pucchGroupHopping = 0; + hoppingId = 40; + p0_nominal = -90; + + ssb_PositionsInBurst_Bitmap = 1; + +# ssb_periodicityServingCell +# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1 + ssb_periodicityServingCell = 2; + +# dmrs_TypeA_position +# 0 = pos2, 1 = pos3 + dmrs_TypeA_Position = 0; + +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + subcarrierSpacing = 0; + + + #tdd-UL-DL-ConfigurationCommon +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + referenceSubcarrierSpacing = 0; + # pattern1 + # dl_UL_TransmissionPeriodicity + # 0=ms0p5, 1=ms0p625, 2=ms1, 3=ms1p25, 4=ms2, 5=ms2p5, 6=ms5, 7=ms10 + dl_UL_TransmissionPeriodicity = 6; + nrofDownlinkSlots = 7; + nrofDownlinkSymbols = 6; + nrofUplinkSlots = 2; + nrofUplinkSymbols = 4; + + ssPBCH_BlockPower = -25; + } + + ); + + # ------- SCTP definitions + SCTP : + { + # Number of streams to use in input/output + SCTP_INSTREAMS = 2; + SCTP_OUTSTREAMS = 2; + }; + } +); + +MACRLCs = ({ + num_cc = 1; + tr_s_preference = "local_L1"; + tr_n_preference = "f1"; + local_n_if_name = "lo"; + local_n_address = "127.0.0.5"; + remote_n_address = "127.0.0.3"; + local_n_portc = 500; + local_n_portd = 2152; + remote_n_portc = 501; + remote_n_portd = 2152; + pusch_FailureThres = 1000; +}); + +L1s = ({ + num_cc = 1; + tr_n_preference = "local_mac"; + prach_dtx_threshold = 200; + pucch0_dtx_threshold = 150; + ofdm_offset_divisor = 8; #set this to UINT_MAX for offset 0 +}); + +RUs = ({ + local_rf = "yes" + nb_tx = 1 + nb_rx = 1 + att_tx = 0 + att_rx = 0; + bands = [66]; + max_pdschReferenceSignalPower = -27; + max_rxgain = 114; + eNB_instances = [0]; + clock_src = "internal"; +}); + +rfsimulator: { + serveraddr = "server"; + serverport = 4043; + options = (); #("saviq"); or/and "chanmod" + modelname = "AWGN"; + IQfile = "/tmp/rfsimulator.iqs" +} + +log_config: { + global_log_level = "info"; + hw_log_level = "info"; + nr_phy_log_level = "info"; + nr_mac_log_level = "info"; + rlc_log_level = "info"; + pdcp_log_level = "info"; + rrc_log_level = "info"; + f1ap_log_level = "info"; + ngap_log_level = "debug"; +}; diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band78.106prb.rfsim.pci0.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band78.106prb.rfsim.pci0.conf new file mode 100644 index 0000000000..2e0f721504 --- /dev/null +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band78.106prb.rfsim.pci0.conf @@ -0,0 +1,212 @@ +Active_gNBs = ( "du-rfsim"); +# Asn1_verbosity, choice in: none, info, annoying +Asn1_verbosity = "none"; + +gNBs = +( + { + ////////// Identification parameters: + gNB_ID = 0xe00; + gNB_DU_ID = 0xe00; + gNB_name = "du-rfsim"; + + // Tracking area code, 0x0000 and 0xfffe are reserved values + tracking_area_code = 1; + plmn_list = ({ mcc = 001; mnc = 01; mnc_length = 2; snssaiList = ({ sst = 1; }) }); + + nr_cellid = 12345678L; + + ////////// Physical parameters: + + min_rxtxtime = 4; + + servingCellConfigCommon = ( + { + #spCellConfigCommon + + physCellId = 0; + +# downlinkConfigCommon + #frequencyInfoDL + # this is 3600 MHz + 43 PRBs@30kHz SCS (same as initial BWP) + absoluteFrequencySSB = 630048; + dl_frequencyBand = 78; + # this is 3600 MHz + dl_absoluteFrequencyPointA = 628776; + #scs-SpecificCarrierList + dl_offstToCarrier = 0; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + dl_subcarrierSpacing = 1; + dl_carrierBandwidth = 106; + #initialDownlinkBWP + #genericParameters + # this is RBstart=27,L=48 (275*(L-1))+RBstart + initialDLBWPlocationAndBandwidth = 28875; # 6366 12925 12956 28875 12952 +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + initialDLBWPsubcarrierSpacing = 1; + #pdcch-ConfigCommon + initialDLBWPcontrolResourceSetZero = 12; + initialDLBWPsearchSpaceZero = 0; + + #uplinkConfigCommon + #frequencyInfoUL + ul_frequencyBand = 78; + #scs-SpecificCarrierList + ul_offstToCarrier = 0; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + ul_subcarrierSpacing = 1; + ul_carrierBandwidth = 106; + pMax = 20; + #initialUplinkBWP + #genericParameters + initialULBWPlocationAndBandwidth = 28875; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + initialULBWPsubcarrierSpacing = 1; + #rach-ConfigCommon + #rach-ConfigGeneric + prach_ConfigurationIndex = 98; +#prach_msg1_FDM +#0 = one, 1=two, 2=four, 3=eight + prach_msg1_FDM = 0; + prach_msg1_FrequencyStart = 0; + zeroCorrelationZoneConfig = 13; + preambleReceivedTargetPower = -96; +#preamblTransMax (0...10) = (3,4,5,6,7,8,10,20,50,100,200) + preambleTransMax = 6; +#powerRampingStep +# 0=dB0,1=dB2,2=dB4,3=dB6 + powerRampingStep = 1; +#ra_ReponseWindow +#1,2,4,8,10,20,40,80 + ra_ResponseWindow = 4; +#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR +#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen + ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4; +#oneHalf (0..15) 4,8,12,16,...60,64 + ssb_perRACH_OccasionAndCB_PreamblesPerSSB = 14; +#ra_ContentionResolutionTimer +#(0..7) 8,16,24,32,40,48,56,64 + ra_ContentionResolutionTimer = 7; + rsrp_ThresholdSSB = 19; +#prach-RootSequenceIndex_PR +#1 = 839, 2 = 139 + prach_RootSequenceIndex_PR = 2; + prach_RootSequenceIndex = 1; + # SCS for msg1, can only be 15 for 30 kHz < 6 GHz, takes precendence over the one derived from prach-ConfigIndex + # + msg1_SubcarrierSpacing = 1, +# restrictedSetConfig +# 0=unrestricted, 1=restricted type A, 2=restricted type B + restrictedSetConfig = 0, + + msg3_DeltaPreamble = 1; + p0_NominalWithGrant =-90; + +# pucch-ConfigCommon setup : +# pucchGroupHopping +# 0 = neither, 1= group hopping, 2=sequence hopping + pucchGroupHopping = 0; + hoppingId = 40; + p0_nominal = -90; + + ssb_PositionsInBurst_Bitmap = 1; + +# ssb_periodicityServingCell +# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1 + ssb_periodicityServingCell = 2; + +# dmrs_TypeA_position +# 0 = pos2, 1 = pos3 + dmrs_TypeA_Position = 0; + +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + subcarrierSpacing = 1; + + + #tdd-UL-DL-ConfigurationCommon +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + referenceSubcarrierSpacing = 1; + # pattern1 + # dl_UL_TransmissionPeriodicity + # 0=ms0p5, 1=ms0p625, 2=ms1, 3=ms1p25, 4=ms2, 5=ms2p5, 6=ms5, 7=ms10 + dl_UL_TransmissionPeriodicity = 6; + nrofDownlinkSlots = 7; + nrofDownlinkSymbols = 6; + nrofUplinkSlots = 2; + nrofUplinkSymbols = 4; + + ssPBCH_BlockPower = -25; + } + + ); + + # ------- SCTP definitions + SCTP : + { + # Number of streams to use in input/output + SCTP_INSTREAMS = 2; + SCTP_OUTSTREAMS = 2; + }; + } +); + +MACRLCs = ({ + num_cc = 1; + tr_s_preference = "local_L1"; + tr_n_preference = "f1"; + local_n_if_name = "lo"; + local_n_address = "127.0.0.4"; + remote_n_address = "127.0.0.3"; + local_n_portc = 500; + local_n_portd = 2152; + remote_n_portc = 501; + remote_n_portd = 2152; + pusch_FailureThres = 1000; +}); + +L1s = ({ + num_cc = 1; + tr_n_preference = "local_mac"; + prach_dtx_threshold = 200; + pucch0_dtx_threshold = 150; + ofdm_offset_divisor = 8; #set this to UINT_MAX for offset 0 +}); + +RUs = ({ + local_rf = "yes" + nb_tx = 1 + nb_rx = 1 + att_tx = 0 + att_rx = 0; + bands = [78]; + max_pdschReferenceSignalPower = -27; + max_rxgain = 114; + eNB_instances = [0]; + clock_src = "internal"; +}); + +rfsimulator: { + serveraddr = "server"; + serverport = 4043; + options = (); #("saviq"); or/and "chanmod" + modelname = "AWGN"; + IQfile = "/tmp/rfsimulator.iqs" +} + +log_config: { + global_log_level = "info"; + hw_log_level = "info"; + nr_phy_log_level = "info"; + nr_mac_log_level = "info"; + rlc_log_level = "info"; + pdcp_log_level = "info"; + rrc_log_level = "info"; + f1ap_log_level = "info"; + ngap_log_level = "debug"; +}; diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band78.106prb.rfsim.pci1.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band78.106prb.rfsim.pci1.conf new file mode 100644 index 0000000000..8e939887ce --- /dev/null +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band78.106prb.rfsim.pci1.conf @@ -0,0 +1,212 @@ +Active_gNBs = ( "du-rfsim"); +# Asn1_verbosity, choice in: none, info, annoying +Asn1_verbosity = "none"; + +gNBs = +( + { + ////////// Identification parameters: + gNB_ID = 0xe00; + gNB_DU_ID = 0xe01; + gNB_name = "du-rfsim"; + + // Tracking area code, 0x0000 and 0xfffe are reserved values + tracking_area_code = 1; + plmn_list = ({ mcc = 001; mnc = 01; mnc_length = 2; snssaiList = ({ sst = 1; }) }); + + nr_cellid = 11111111L; + + ////////// Physical parameters: + + min_rxtxtime = 4; + + servingCellConfigCommon = ( + { + #spCellConfigCommon + + physCellId = 1; + +# downlinkConfigCommon + #frequencyInfoDL + # this is 3600 MHz + 43 PRBs@30kHz SCS (same as initial BWP) + absoluteFrequencySSB = 643296; + dl_frequencyBand = 78; + # this is 3600 MHz + dl_absoluteFrequencyPointA = 642024; + #scs-SpecificCarrierList + dl_offstToCarrier = 0; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + dl_subcarrierSpacing = 1; + dl_carrierBandwidth = 106; + #initialDownlinkBWP + #genericParameters + # this is RBstart=27,L=48 (275*(L-1))+RBstart + initialDLBWPlocationAndBandwidth = 28875; # 6366 12925 12956 28875 12952 +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + initialDLBWPsubcarrierSpacing = 1; + #pdcch-ConfigCommon + initialDLBWPcontrolResourceSetZero = 12; + initialDLBWPsearchSpaceZero = 0; + + #uplinkConfigCommon + #frequencyInfoUL + ul_frequencyBand = 78; + #scs-SpecificCarrierList + ul_offstToCarrier = 0; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + ul_subcarrierSpacing = 1; + ul_carrierBandwidth = 106; + pMax = 20; + #initialUplinkBWP + #genericParameters + initialULBWPlocationAndBandwidth = 28875; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + initialULBWPsubcarrierSpacing = 1; + #rach-ConfigCommon + #rach-ConfigGeneric + prach_ConfigurationIndex = 98; +#prach_msg1_FDM +#0 = one, 1=two, 2=four, 3=eight + prach_msg1_FDM = 0; + prach_msg1_FrequencyStart = 0; + zeroCorrelationZoneConfig = 13; + preambleReceivedTargetPower = -96; +#preamblTransMax (0...10) = (3,4,5,6,7,8,10,20,50,100,200) + preambleTransMax = 6; +#powerRampingStep +# 0=dB0,1=dB2,2=dB4,3=dB6 + powerRampingStep = 1; +#ra_ReponseWindow +#1,2,4,8,10,20,40,80 + ra_ResponseWindow = 4; +#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR +#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen + ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4; +#oneHalf (0..15) 4,8,12,16,...60,64 + ssb_perRACH_OccasionAndCB_PreamblesPerSSB = 14; +#ra_ContentionResolutionTimer +#(0..7) 8,16,24,32,40,48,56,64 + ra_ContentionResolutionTimer = 7; + rsrp_ThresholdSSB = 19; +#prach-RootSequenceIndex_PR +#1 = 839, 2 = 139 + prach_RootSequenceIndex_PR = 2; + prach_RootSequenceIndex = 1; + # SCS for msg1, can only be 15 for 30 kHz < 6 GHz, takes precendence over the one derived from prach-ConfigIndex + # + msg1_SubcarrierSpacing = 1, +# restrictedSetConfig +# 0=unrestricted, 1=restricted type A, 2=restricted type B + restrictedSetConfig = 0, + + msg3_DeltaPreamble = 1; + p0_NominalWithGrant =-90; + +# pucch-ConfigCommon setup : +# pucchGroupHopping +# 0 = neither, 1= group hopping, 2=sequence hopping + pucchGroupHopping = 0; + hoppingId = 40; + p0_nominal = -90; + + ssb_PositionsInBurst_Bitmap = 3; + +# ssb_periodicityServingCell +# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1 + ssb_periodicityServingCell = 2; + +# dmrs_TypeA_position +# 0 = pos2, 1 = pos3 + dmrs_TypeA_Position = 0; + +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + subcarrierSpacing = 1; + + + #tdd-UL-DL-ConfigurationCommon +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + referenceSubcarrierSpacing = 1; + # pattern1 + # dl_UL_TransmissionPeriodicity + # 0=ms0p5, 1=ms0p625, 2=ms1, 3=ms1p25, 4=ms2, 5=ms2p5, 6=ms5, 7=ms10 + dl_UL_TransmissionPeriodicity = 6; + nrofDownlinkSlots = 7; + nrofDownlinkSymbols = 6; + nrofUplinkSlots = 2; + nrofUplinkSymbols = 4; + + ssPBCH_BlockPower = -25; + } + + ); + + # ------- SCTP definitions + SCTP : + { + # Number of streams to use in input/output + SCTP_INSTREAMS = 2; + SCTP_OUTSTREAMS = 2; + }; + } +); + +MACRLCs = ({ + num_cc = 1; + tr_s_preference = "local_L1"; + tr_n_preference = "f1"; + local_n_if_name = "lo"; + local_n_address = "127.0.0.5"; + remote_n_address = "127.0.0.3"; + local_n_portc = 500; + local_n_portd = 2152; + remote_n_portc = 501; + remote_n_portd = 2152; + pusch_FailureThres = 1000; +}); + +L1s = ({ + num_cc = 1; + tr_n_preference = "local_mac"; + prach_dtx_threshold = 200; + pucch0_dtx_threshold = 150; + ofdm_offset_divisor = 8; #set this to UINT_MAX for offset 0 +}); + +RUs = ({ + local_rf = "yes" + nb_tx = 1 + nb_rx = 1 + att_tx = 0 + att_rx = 0; + bands = [78]; + max_pdschReferenceSignalPower = -27; + max_rxgain = 114; + eNB_instances = [0]; + clock_src = "internal"; +}); + +rfsimulator: { + serveraddr = "server"; + serverport = 4043; + options = (); #("saviq"); or/and "chanmod" + modelname = "AWGN"; + IQfile = "/tmp/rfsimulator.iqs" +} + +log_config: { + global_log_level = "info"; + hw_log_level = "info"; + nr_phy_log_level = "info"; + nr_mac_log_level = "info"; + rlc_log_level = "info"; + pdcp_log_level = "info"; + rrc_log_level = "info"; + f1ap_log_level = "info"; + ngap_log_level = "debug"; +}; From ecd941e9466edac9379dcf3d974c5763437690d9 Mon Sep 17 00:00:00 2001 From: rmagueta Date: Wed, 5 Jun 2024 08:11:56 +0200 Subject: [PATCH 16/41] Remove old RA process(es) when removing UE In handover, we might need to remove a UE that we knew under it's C-RNTI, for which we also have the RA process. Hence, when removing a UE by RNTI, remove it also from RA. --- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index 0b09bb78a9..7ac6d28fb8 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -2675,6 +2675,16 @@ void mac_remove_nr_ue(gNB_MAC_INST *nr_mac, rnti_t rnti) memcpy(UE_info->list, newUEs, sizeof(UE_info->list)); NR_SCHED_UNLOCK(&UE_info->mutex); + const int CC_id = 0; + NR_COMMON_channels_t *cc = &nr_mac->common_channels[CC_id]; + for (int i = 0; i < NR_NB_RA_PROC_MAX; i++) { + NR_RA_t *ra = &cc->ra[i]; + if (ra->rnti == UE->rnti) { + nr_clear_ra_proc(ra); + ra->cfra = 0; + } + } + delete_nr_ue_data(UE, nr_mac->common_channels, &UE_info->uid_allocator); } From c3ef6df44bf9e3460701b68f0046e73a1851c060 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 8 Oct 2024 10:53:56 +0200 Subject: [PATCH 17/41] Bugfix: request release in case of failed RA Prior to this commit, after RA failure, the DU would send a UE context release complete, without actually requesting one. This could lead to inconsistent state between DU and CU, because the CU might just discard this (unrequested) message, and not actually trigger a release [1]. Thus, refactor the release request into a function, and use it to request release of a UE for which RA has not been completed. If the CU does not know the UE, release the UE immediately. [1] One possibility would be that during reestablishment, the CU would request to look up an old DU UE ID when it has been released, resulting in Assertion (oldUE) failed! In dl_rrc_message_transfer() /home/oaicicd/robert/openairinterface5g/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c:860 CU claims we should know UE bc7f, but we don't --- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c | 5 +-- .../NR_MAC_gNB/gNB_scheduler_primitives.c | 33 ++++++++++++++----- openair2/LAYER2/NR_MAC_gNB/mac_proto.h | 3 +- 3 files changed, 29 insertions(+), 12 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c index e43123b55c..3e738db42a 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c @@ -2435,8 +2435,9 @@ void nr_schedule_RA(module_id_t module_idP, ra->contention_resolution_timer--; if (ra->contention_resolution_timer < 0) { LOG_W(NR_MAC, "(%d.%d) RA Contention Resolution timer expired for UE 0x%04x, RA procedure failed...\n", frameP, slotP, ra->rnti); - nr_mac_release_ue(mac, ra->rnti); - nr_mac_trigger_release_complete(mac, ra->rnti); + bool requested = nr_mac_request_release_ue(mac, ra->rnti); + if (!requested) + nr_mac_release_ue(mac, ra->rnti); nr_clear_ra_proc(ra); continue; } diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index 7ac6d28fb8..5930ef7f44 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -3302,7 +3302,27 @@ void nr_mac_reset_ul_failure(NR_UE_sched_ctrl_t *sched_ctrl) sched_ctrl->pusch_consecutive_dtx_cnt = 0; } -void nr_mac_check_ul_failure(const gNB_MAC_INST *nrmac, int rnti, NR_UE_sched_ctrl_t *sched_ctrl) +/* \brief trigger a release request towards the CU. + * \returns true if release was requested, else false (in which case the CU + * does not know the UE, and we can safely remove it). */ +bool nr_mac_request_release_ue(const gNB_MAC_INST *nrmac, int rnti) +{ + if (!du_exists_f1_ue_data(rnti)) { + LOG_W(NR_MAC, "UE %04x: no CU-UE ID stored, cannot request release\n", rnti); + return false; + } + f1_ue_data_t ue_data = du_get_f1_ue_data(rnti); + f1ap_ue_context_release_req_t request = { + .gNB_CU_ue_id = ue_data.secondary_ue, + .gNB_DU_ue_id = rnti, + .cause = F1AP_CAUSE_RADIO_NETWORK, + .cause_value = F1AP_CauseRadioNetwork_rl_failure_others, + }; + nrmac->mac_rrc.ue_context_release_request(&request); + return true; +} + +void nr_mac_check_ul_failure(gNB_MAC_INST *nrmac, int rnti, NR_UE_sched_ctrl_t *sched_ctrl) { if (!sched_ctrl->ul_failure) return; @@ -3312,14 +3332,9 @@ void nr_mac_check_ul_failure(const gNB_MAC_INST *nrmac, int rnti, NR_UE_sched_ct * stop at 0 and we wait for a UE release command from upper layers */ if (sched_ctrl->ul_failure_timer == 1) { LOG_W(MAC, "UE %04x: request release after UL failure timer expiry\n", rnti); - f1_ue_data_t ue_data = du_get_f1_ue_data(rnti); - f1ap_ue_context_release_req_t request = { - .gNB_CU_ue_id = ue_data.secondary_ue, - .gNB_DU_ue_id = rnti, - .cause = F1AP_CAUSE_RADIO_NETWORK, - .cause_value = F1AP_CauseRadioNetwork_rl_failure_others, - }; - nrmac->mac_rrc.ue_context_release_request(&request); + bool requested = nr_mac_request_release_ue(nrmac, rnti); + if (!requested) + nr_mac_release_ue(nrmac, rnti); } } diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h index a75b9c0f60..c5df1782fd 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h +++ b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h @@ -464,10 +464,11 @@ void nr_mac_trigger_release_timer(NR_UE_sched_ctrl_t *sched_ctrl, NR_SubcarrierS bool nr_mac_check_release(NR_UE_sched_ctrl_t *sched_ctrl, int rnti); void nr_mac_trigger_release_complete(gNB_MAC_INST *mac, int rnti); void nr_mac_release_ue(gNB_MAC_INST *mac, int rnti); +bool nr_mac_request_release_ue(const gNB_MAC_INST *nrmac, int rnti); void nr_mac_trigger_ul_failure(NR_UE_sched_ctrl_t *sched_ctrl, NR_SubcarrierSpacing_t subcarrier_spacing); void nr_mac_reset_ul_failure(NR_UE_sched_ctrl_t *sched_ctrl); -void nr_mac_check_ul_failure(const gNB_MAC_INST *nrmac, int rnti, NR_UE_sched_ctrl_t *sched_ctrl); +void nr_mac_check_ul_failure(gNB_MAC_INST *nrmac, int rnti, NR_UE_sched_ctrl_t *sched_ctrl); void nr_mac_trigger_reconfiguration(const gNB_MAC_INST *nrmac, const NR_UE_info_t *UE); From 67eb94db3ffaaa5fd1c4f32926bc1a1aefb69ccf Mon Sep 17 00:00:00 2001 From: Roberto Magueta Date: Mon, 2 Sep 2024 16:37:16 +0200 Subject: [PATCH 18/41] Print RRC neighbor cell measurements, if present Co-authored-by: Luis Pereira --- openair2/RRC/NR/rrc_gNB.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index 9803991db5..9e3cd6aa1d 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -2194,6 +2194,24 @@ static void print_rrc_meas(FILE *f, const NR_MeasResults_t *measresults) } else { fprintf(f, "NOT PROVIDED\n"); } + + if (measresults->measResultNeighCells + && measresults->measResultNeighCells->present == NR_MeasResults__measResultNeighCells_PR_measResultListNR) { + NR_MeasResultListNR_t *meas_neigh = measresults->measResultNeighCells->choice.measResultListNR; + for (int i = 0; i < meas_neigh->list.count; ++i) { + NR_MeasResultNR_t *measresultneigh = meas_neigh->list.array[i]; + NR_MeasQuantityResults_t *neigh_mqr = measresultneigh->measResult.cellResults.resultsSSB_Cell; + fprintf(f, " neighboring cell for phyCellId %ld:\n resultSSB:", *measresultneigh->physCellId); + if (mqr != NULL) { + const long rrsrp = *neigh_mqr->rsrp - 156; + const float rrsrq = (float)(*neigh_mqr->rsrq - 87) / 2.0f; + const float rsinr = (float)(*neigh_mqr->sinr - 46) / 2.0f; + fprintf(f, "RSRP %ld dBm RSRQ %.1f dB SINR %.1f dB\n", rrsrp, rrsrq, rsinr); + } else { + fprintf(f, "NOT PROVIDED\n"); + } + } + } } static const char *get_pdusession_status_text(pdu_session_status_t status) From 6cd76ff862491fe9742e2673a6b1fbdab82f8bbe Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 1 Oct 2024 19:30:02 +0200 Subject: [PATCH 19/41] Remove unused includes and comment with resolved issue --- openair2/RRC/NR/nr_rrc_defs.h | 8 -------- 1 file changed, 8 deletions(-) diff --git a/openair2/RRC/NR/nr_rrc_defs.h b/openair2/RRC/NR/nr_rrc_defs.h index b5b27a6f4d..aebbed95b0 100644 --- a/openair2/RRC/NR/nr_rrc_defs.h +++ b/openair2/RRC/NR/nr_rrc_defs.h @@ -69,14 +69,6 @@ #include "intertask_interface.h" -/* TODO: be sure this include is correct. - * It solves a problem of compilation of the RRH GW, - * issue #186. - */ - #include "as_message.h" - - #include "commonDef.h" - #define PROTOCOL_NR_RRC_CTXT_UE_FMT PROTOCOL_CTXT_FMT #define PROTOCOL_NR_RRC_CTXT_UE_ARGS(CTXT_Pp) PROTOCOL_NR_CTXT_ARGS(CTXT_Pp) From 35995c8a10c4c591e0bbd07ef67e3c3fec8f86e2 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Fri, 4 Oct 2024 19:38:12 +0200 Subject: [PATCH 20/41] Move RETURN_IF_INVALID_ASSOC_ID() to header for use elsewhere RETURN_IF_INVALID_ASSOC_ID() checks if an assoc_id (of a DU) is valid. It will be also used in the mobility RRC module later. Move it to the DU header (where it logically belongs) to move it from there. --- openair2/RRC/NR/rrc_gNB.c | 10 ---------- openair2/RRC/NR/rrc_gNB_du.h | 10 ++++++++++ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index 9e3cd6aa1d..840844a0ec 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -112,16 +112,6 @@ extern RAN_CONTEXT_t RC; mui_t rrc_gNB_mui = 0; -// the assoc_id might be 0 (if the DU goes offline). Below helper macro to -// print an error and return from the function in that case -#define RETURN_IF_INVALID_ASSOC_ID(assoc_id) \ - { \ - if (assoc_id == 0) { \ - LOG_E(NR_RRC, "cannot send data: invalid assoc_id 0, DU offline\n"); \ - return; \ - } \ - } - ///---------------------------------------------------------------------------------------------------------------/// ///---------------------------------------------------------------------------------------------------------------/// diff --git a/openair2/RRC/NR/rrc_gNB_du.h b/openair2/RRC/NR/rrc_gNB_du.h index 3ee0b611ef..2c0b5574ce 100644 --- a/openair2/RRC/NR/rrc_gNB_du.h +++ b/openair2/RRC/NR/rrc_gNB_du.h @@ -48,4 +48,14 @@ int get_dl_band(const struct f1ap_served_cell_info_t *cell_info); int get_ssb_scs(const struct f1ap_served_cell_info_t *cell_info); int get_ssb_arfcn(const struct nr_rrc_du_container_t *du); +// the assoc_id might be 0 (if the DU goes offline). Below helper macro to +// print an error and return from the function in that case +#define RETURN_IF_INVALID_ASSOC_ID(assoc_id) \ + { \ + if (assoc_id == 0) { \ + LOG_E(NR_RRC, "cannot send data: invalid assoc_id 0, DU offline\n"); \ + return; \ + } \ + } + #endif /* RRC_GNB_DU_H_ */ From b5e35077649f80bc828a55263d26ad77b8629d2e Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 22 Oct 2024 13:43:19 +0200 Subject: [PATCH 21/41] Fix: store CellGroupConfig from initial UL RRC message This F1 message sends us the CellGroupConfig of the UE. Store it in case we need it. For instance, at least in OAI, it is possible to make a handover without a DRB (this might or might not be standard compliant). However, in this case, we did never receive a UE context setup request, which stores the CellGroupConfig. Hence, store the first CellGroupConfig we receive, to be able to do e.g., handover. --- openair2/RRC/NR/rrc_gNB.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index 840844a0ec..7b0333db85 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -1029,9 +1029,18 @@ static void rrc_handle_RRCSetupRequest(gNB_RRC_INST *rrc, return; } + NR_CellGroupConfig_t *cellGroupConfig = NULL; + asn_dec_rval_t dec_rval = uper_decode_complete(NULL, + &asn_DEF_NR_CellGroupConfig, + (void **)&cellGroupConfig, + msg->du2cu_rrc_container, + msg->du2cu_rrc_container_length); + AssertFatal(dec_rval.code == RC_OK && dec_rval.consumed > 0, "Cell group config decode error\n"); + gNB_RRC_UE_t *UE = &ue_context_p->ue_context; UE = &ue_context_p->ue_context; UE->establishment_cause = rrcSetupRequest->establishmentCause; + UE->masterCellGroup = cellGroupConfig; activate_srb(UE, 1); rrc_gNB_generate_RRCSetup(0, msg->crnti, ue_context_p, msg->du2cu_rrc_container, msg->du2cu_rrc_container_length); } From b4bf59c55bce0c66d5a4055fb5d7b7ca2e2227fb Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 4 Jun 2024 13:51:21 +0200 Subject: [PATCH 22/41] Add telnet module function to trigger F1 HO --- common/utils/telnetsrv/telnetsrv_ci.c | 81 ++++++++++++++++++++------- 1 file changed, 62 insertions(+), 19 deletions(-) diff --git a/common/utils/telnetsrv/telnetsrv_ci.c b/common/utils/telnetsrv/telnetsrv_ci.c index 9f3c03c39a..700ff73135 100644 --- a/common/utils/telnetsrv/telnetsrv_ci.c +++ b/common/utils/telnetsrv/telnetsrv_ci.c @@ -71,30 +71,41 @@ int get_single_rnti(char *buf, int debug, telnet_printfunc_t prnt) return 0; } +rrc_gNB_ue_context_t *get_single_rrc_ue(void) +{ + rrc_gNB_ue_context_t *ue = NULL; + rrc_gNB_ue_context_t *l = NULL; + int n = 0; + RB_FOREACH (l, rrc_nr_ue_tree_s, &RC.nrrrc[0]->rrc_ue_head) { + if (ue == NULL) + ue = l; + n++; + } + if (!ue) { + printf("could not find any UE in RRC\n"); + } + if (n > 1) { + printf("more than one UE in RRC present\n"); + ue = NULL; + } + + return ue; +} + int get_reestab_count(char *buf, int debug, telnet_printfunc_t prnt) { if (!RC.nrrrc) ERROR_MSG_RET("no RRC present, cannot list counts\n"); rrc_gNB_ue_context_t *ue = NULL; - int rnti = -1; if (!buf) { - rrc_gNB_ue_context_t *l = NULL; - int n = 0; - RB_FOREACH(l, rrc_nr_ue_tree_s, &RC.nrrrc[0]->rrc_ue_head) { - if (ue == NULL) ue = l; - n++; - } + ue = get_single_rrc_ue(); if (!ue) - ERROR_MSG_RET("could not find any UE in RRC\n"); - if (n > 1) - ERROR_MSG_RET("more than one UE in RRC present\n"); + ERROR_MSG_RET("no single UE in RRC present\n"); } else { - rnti = strtol(buf, NULL, 16); - if (rnti < 1 || rnti >= 0xfffe) - ERROR_MSG_RET("RNTI needs to be [1,0xfffe]\n"); - ue = rrc_gNB_get_ue_context_by_rnti_any_du(RC.nrrrc[0], rnti); + ue_id_t ue_id = strtol(buf, NULL, 10); + ue = rrc_gNB_get_ue_context(RC.nrrrc[0], ue_id); if (!ue) - ERROR_MSG_RET("could not find UE with RNTI %04x in RRC\n"); + ERROR_MSG_RET("could not find UE with ue_id %d in RRC\n"); } prnt("UE RNTI %04x reestab %d reconfig %d\n", @@ -125,11 +136,43 @@ int trigger_reestab(char *buf, int debug, telnet_printfunc_t prnt) return 0; } +extern void nr_HO_F1_trigger_telnet(gNB_RRC_INST *rrc, uint32_t rrc_ue_id); +/** + * @brief Trigger F1 handover for UE + * @param buf: RRC UE ID or NULL for the first UE in list + * @param debug: Debug flag + * @param prnt: Print function + * @return 0 on success, -1 on failure + */ +int rrc_gNB_trigger_f1_ho(char *buf, int debug, telnet_printfunc_t prnt) +{ + if (!RC.nrrrc) + ERROR_MSG_RET("no RRC present, cannot list counts\n"); + rrc_gNB_ue_context_t *ue = NULL; + if (!buf) { + ue = get_single_rrc_ue(); + if (!ue) + ERROR_MSG_RET("no single UE in RRC present\n"); + } else { + ue_id_t ue_id = strtol(buf, NULL, 10); + ue = rrc_gNB_get_ue_context(RC.nrrrc[0], ue_id); + if (!ue) + ERROR_MSG_RET("could not find UE with ue_id %d in RRC\n", ue_id); + } + + gNB_RRC_UE_t *UE = &ue->ue_context; + nr_HO_F1_trigger_telnet(RC.nrrrc[0], UE->rrc_ue_id); + prnt("RRC F1 handover triggered for UE %u\n", UE->rrc_ue_id); + + return 0; +} + static telnetshell_cmddef_t cicmds[] = { - {"get_single_rnti", "", get_single_rnti}, - {"force_reestab", "[rnti(hex,opt)]", trigger_reestab}, - {"get_reestab_count", "[rnti(hex,opt)]", get_reestab_count}, - {"", "", NULL}, + {"get_single_rnti", "", get_single_rnti}, + {"force_reestab", "[rnti(hex,opt)]", trigger_reestab}, + {"get_reestab_count", "[rnti(hex,opt)]", get_reestab_count}, + {"trigger_f1_ho", "[rrc_ue_id(int,opt)]", rrc_gNB_trigger_f1_ho}, + {"", "", NULL}, }; static telnetshell_vardef_t civars[] = { From 90d8eefa591c0935a6b4ba814ff256bbc211c9af Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 25 Jul 2024 13:43:20 +0200 Subject: [PATCH 23/41] Add new handover context structures for handover operation --- CMakeLists.txt | 1 + openair2/RRC/NR/nr_rrc_defs.h | 22 ++-------- openair2/RRC/NR/rrc_gNB.c | 1 + openair2/RRC/NR/rrc_gNB_mobility.c | 48 +++++++++++++++++++++ openair2/RRC/NR/rrc_gNB_mobility.h | 69 ++++++++++++++++++++++++++++++ 5 files changed, 123 insertions(+), 18 deletions(-) create mode 100644 openair2/RRC/NR/rrc_gNB_mobility.c create mode 100644 openair2/RRC/NR/rrc_gNB_mobility.h diff --git a/CMakeLists.txt b/CMakeLists.txt index af48eca7b3..c34e3cbbd7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1271,6 +1271,7 @@ set(L2_NR_SRC ${NR_RRC_DIR}/rrc_gNB_radio_bearers.c ${NR_RRC_DIR}/rrc_gNB_cuup.c ${NR_RRC_DIR}/rrc_gNB_du.c + ${NR_RRC_DIR}/rrc_gNB_mobility.c ) set(L2_SRC_UE diff --git a/openair2/RRC/NR/nr_rrc_defs.h b/openair2/RRC/NR/nr_rrc_defs.h index aebbed95b0..14d08e9c45 100644 --- a/openair2/RRC/NR/nr_rrc_defs.h +++ b/openair2/RRC/NR/nr_rrc_defs.h @@ -112,23 +112,6 @@ typedef struct nr_e_rab_param_s { uint8_t xid; // transaction_id } __attribute__ ((__packed__)) nr_e_rab_param_t; - -typedef struct HANDOVER_INFO_NR_s { - uint8_t ho_prepare; - uint8_t ho_complete; - uint8_t modid_s; //module_idP of serving cell - uint8_t modid_t; //module_idP of target cell - uint8_t ueid_s; //UE index in serving cell - uint8_t ueid_t; //UE index in target cell - - // NR not define at this moment - //AS_Config_t as_config; /* these two parameters are taken from 36.331 section 10.2.2: HandoverPreparationInformation-r8-IEs */ - //AS_Context_t as_context; /* They are mandatory for HO */ - - uint8_t buf[NR_RRC_BUF_SIZE]; /* ASN.1 encoded handoverCommandMessage */ - int size; /* size of above message in bytes */ -} NR_HANDOVER_INFO; - typedef struct nr_rrc_guami_s { uint16_t mcc; uint16_t mnc; @@ -214,6 +197,9 @@ typedef enum { RRC_UECAPABILITY_ENQUIRY, } rrc_action_t; +/* forward declaration */ +typedef struct nr_handover_context_s nr_handover_context_t; + typedef struct gNB_RRC_UE_s { time_t last_seen; // last time this UE has been accessed @@ -222,7 +208,7 @@ typedef struct gNB_RRC_UE_s { NR_SRB_INFO_TABLE_ENTRY Srb[NR_NUM_SRB]; NR_MeasConfig_t *measConfig; - NR_HANDOVER_INFO *handover_info; + nr_handover_context_t *ho_context; NR_MeasResults_t *measResults; bool as_security_active; diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index 7b0333db85..df865c02a8 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -77,6 +77,7 @@ #include "rrc_gNB_NGAP.h" #include "rrc_gNB_du.h" +#include "rrc_gNB_mobility.h" #include "rrc_gNB_GTPV1U.h" diff --git a/openair2/RRC/NR/rrc_gNB_mobility.c b/openair2/RRC/NR/rrc_gNB_mobility.c new file mode 100644 index 0000000000..72ae57b67e --- /dev/null +++ b/openair2/RRC/NR/rrc_gNB_mobility.c @@ -0,0 +1,48 @@ +/* + * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The OpenAirInterface Software Alliance licenses this file to You under + * the OAI Public License, Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.openairinterface.org/?page_id=698 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------------------- + * For more information about the OpenAirInterface (OAI) Software Alliance: + * contact@openairinterface.org + */ + +#include + +#include "assertions.h" +#include "rrc_gNB_mobility.h" + +typedef enum { HO_CTX_BOTH, HO_CTX_SOURCE, HO_CTX_TARGET } ho_ctx_type_t; +static nr_handover_context_t *alloc_ho_ctx(ho_ctx_type_t type) +{ + nr_handover_context_t *ho_ctx = calloc(1, sizeof(*ho_ctx)); + AssertFatal(ho_ctx != NULL, "out of memory\n"); + if (type == HO_CTX_SOURCE || type == HO_CTX_BOTH) { + ho_ctx->source = calloc(1, sizeof(*ho_ctx->source)); + AssertFatal(ho_ctx->source != NULL, "out of memory\n"); + } + if (type == HO_CTX_TARGET || type == HO_CTX_BOTH) { + ho_ctx->target = calloc(1, sizeof(*ho_ctx->target)); + AssertFatal(ho_ctx->target != NULL, "out of memory\n"); + } + return ho_ctx; +} + +static void free_ho_ctx(nr_handover_context_t *ho_ctx) +{ + free(ho_ctx->source); + free(ho_ctx->target); + free(ho_ctx); +} diff --git a/openair2/RRC/NR/rrc_gNB_mobility.h b/openair2/RRC/NR/rrc_gNB_mobility.h new file mode 100644 index 0000000000..fefa39982b --- /dev/null +++ b/openair2/RRC/NR/rrc_gNB_mobility.h @@ -0,0 +1,69 @@ +/* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The OpenAirInterface Software Alliance licenses this file to You under + * the OAI Public License, Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.openairinterface.org/?page_id=698 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------------------- + * For more information about the OpenAirInterface (OAI) Software Alliance: + * contact@openairinterface.org + */ + +#ifndef RRC_GNB_MOBILITY_H_ +#define RRC_GNB_MOBILITY_H_ + +#include + +/* forward declarations */ +typedef struct gNB_RRC_INST_s gNB_RRC_INST; +typedef struct gNB_RRC_UE_s gNB_RRC_UE_t; +typedef struct nr_rrc_du_container_t nr_rrc_du_container_t; + +typedef void (*ho_cancel_t)(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue); +typedef struct nr_ho_source_cu { + /// pointer to the (source) DU structure + const nr_rrc_du_container_t *du; + /// (source) DU UE ID; in F1, the DU UE ID will change to the new (target) DU + /// UE ID during handover, and the CU needs to keep track of this in case of + /// reestablishment + uint32_t du_ue_id; + /// old (source) RNTI (to recognize a UE during reestablishment) + rnti_t old_rnti; + /// function pointer to announce the handover cancellation, e.g., + /// reestablishment + ho_cancel_t ho_cancel; +} nr_ho_source_cu_t; + +/* acknowledgement of handover request. buf+len is the RRC Reconfiguration */ +typedef void (*ho_req_ack_t)(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, uint8_t *buf, uint32_t len); +typedef void (*ho_success_t)(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue); +typedef struct nr_ho_target_cu { + /// pointer to the (target) DU structure + const nr_rrc_du_container_t *du; + /// (target) DU UE ID; the (source) DU UE ID will change to the new (target) + /// DU UE ID after sending a reconfiguration, which is later than when + /// receiving this ID + uint32_t du_ue_id; + /// new (target) RNTI (as for du_ue_id) + rnti_t new_rnti; + /// function pointer to announce handover request acknowledgment + ho_req_ack_t ho_req_ack; + /// function pointer to announce handover success + ho_success_t ho_success; +} nr_ho_target_cu_t; + +typedef struct nr_handover_context_s { + nr_ho_source_cu_t *source; + nr_ho_target_cu_t *target; +} nr_handover_context_t; + +#endif /* RRC_GNB_MOBILITY_H_ */ From 746dc29a466001a1fc47ae0a75c2c3aea5312342 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Fri, 4 Oct 2024 20:01:46 +0200 Subject: [PATCH 24/41] Add HandoverPreparationInformation() ASN.1 encoder function HandoverPreparationInformation will be used in the follow-up commit to trigger a handover. Co-authored-by: batuhanduyuler Co-authored-by: rmagueta --- openair2/RRC/NR/MESSAGES/asn1_msg.c | 41 +++++++++++++++++++++++++++++ openair2/RRC/NR/MESSAGES/asn1_msg.h | 2 ++ 2 files changed, 43 insertions(+) diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.c b/openair2/RRC/NR/MESSAGES/asn1_msg.c index 8025fb2243..68a8c49774 100644 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.c +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.c @@ -80,6 +80,8 @@ #include "NR_PCCH-Message.h" #include "NR_PagingRecord.h" #include "NR_UE-CapabilityRequestFilterNR.h" +#include "NR_HandoverPreparationInformation.h" +#include "NR_HandoverPreparationInformation-IEs.h" #include "common/utils/nr/nr_common.h" #if defined(NR_Rel16) #include "NR_SCS-SpecificCarrier.h" @@ -1373,3 +1375,42 @@ int do_NR_Paging(uint8_t Mod_id, uint8_t *buffer, uint32_t tmsi) return((enc_rval.encoded+7)/8); } + +/* \brief generate HandoverPreparationInformation to be sent to the DU for + * handover. Takes uecap_buf in encoded form as (1) this is the form present at + * the CU already (2) we have to clone this information anyway, so can take it + * in encoded form which we decode + add to the handoverPreparationInformation */ +int do_NR_HandoverPreparationInformation(const uint8_t *uecap_buf, int uecap_buf_size, uint8_t *buf, int buf_size) +{ + NR_HandoverPreparationInformation_t *hpi = calloc(1, sizeof(*hpi)); + AssertFatal(hpi != NULL, "out of memory\n"); + hpi->criticalExtensions.present = NR_HandoverPreparationInformation__criticalExtensions_PR_c1; + hpi->criticalExtensions.choice.c1 = calloc(1, sizeof(*hpi->criticalExtensions.choice.c1)); + AssertFatal(hpi->criticalExtensions.choice.c1 != NULL, "out of memory\n"); + hpi->criticalExtensions.choice.c1->present = + NR_HandoverPreparationInformation__criticalExtensions__c1_PR_handoverPreparationInformation; + NR_HandoverPreparationInformation_IEs_t *hpi_ie = calloc(1, sizeof(*hpi_ie)); + AssertFatal(hpi_ie != NULL, "out of memory\n"); + hpi->criticalExtensions.choice.c1->choice.handoverPreparationInformation = hpi_ie; + + NR_UE_CapabilityRAT_ContainerList_t *list = NULL; + asn_dec_rval_t dec_rval = + uper_decode_complete(NULL, &asn_DEF_NR_UE_CapabilityRAT_ContainerList, (void **)&list, uecap_buf, uecap_buf_size); + if (dec_rval.code == RC_OK) { + hpi_ie->ue_CapabilityRAT_List = *list; + free(list); /* list itself is not needed, members below will be freed in ASN_STRUCT_FREE */ + } else { + /* problem with decoding, don't put a capability */ + ASN_STRUCT_FREE(asn_DEF_NR_UE_CapabilityRAT_ContainerList, list); + list = NULL; + } + + if (LOG_DEBUGFLAG(DEBUG_ASN1)) + xer_fprint(stdout, &asn_DEF_NR_HandoverPreparationInformation, hpi); + + asn_enc_rval_t enc_rval = uper_encode_to_buffer(&asn_DEF_NR_HandoverPreparationInformation, NULL, hpi, buf, buf_size); + AssertFatal(enc_rval.encoded > 0, "ASN1 message encoding failed (%s, %lu)!\n", enc_rval.failed_type->name, enc_rval.encoded); + + ASN_STRUCT_FREE(asn_DEF_NR_HandoverPreparationInformation, hpi); + return (enc_rval.encoded + 7) / 8; +} diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.h b/openair2/RRC/NR/MESSAGES/asn1_msg.h index 8609e20328..2f0b4febf8 100644 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.h +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.h @@ -109,6 +109,8 @@ int do_RRCSetupComplete(uint8_t *buffer, const int dedicatedInfoNASLength, const char *dedicatedInfoNAS); +int do_NR_HandoverPreparationInformation(const uint8_t *uecap_buf, int uecap_buf_size, uint8_t *buf, int buf_size); + int do_RRCSetupRequest(uint8_t *buffer, size_t buffer_size, uint8_t *rv); int do_NR_RRCReconfigurationComplete_for_nsa(uint8_t *buffer, size_t buffer_size, NR_RRC_TransactionIdentifier_t Transaction_id); From c0770cc19b52375d615d617033b9d5d565f2e5ad Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Fri, 4 Oct 2024 20:02:44 +0200 Subject: [PATCH 25/41] Make QoS characteristics function available Functions get_qos_char_from_qos_flow_param() and get_qos_characteristics() will be used in rrc_gNB_mobility.c to read PDU information for handover. Move the definitions to a header to reuse later. --- openair2/RRC/NR/nr_rrc_proto.h | 3 +++ openair2/RRC/NR/rrc_gNB.c | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/openair2/RRC/NR/nr_rrc_proto.h b/openair2/RRC/NR/nr_rrc_proto.h index 9c3d276394..576a1f636a 100644 --- a/openair2/RRC/NR/nr_rrc_proto.h +++ b/openair2/RRC/NR/nr_rrc_proto.h @@ -130,4 +130,7 @@ void rrc_gNB_generate_UeContextModificationRequest(const gNB_RRC_INST *rrc, int n_rel_drbs, const f1ap_drb_to_be_released_t *rel_drbs); +pdusession_level_qos_parameter_t *get_qos_characteristics(const int qfi, rrc_pdu_session_param_t *pduSession); +f1ap_qos_characteristics_t get_qos_char_from_qos_flow_param(const pdusession_level_qos_parameter_t *qos_param); + #endif diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index df865c02a8..268dfb7772 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -2023,7 +2023,7 @@ unsigned int mask_flip(unsigned int x) { return((((x>>8) + (x<<8))&0xffff)>>6); } -static pdusession_level_qos_parameter_t *get_qos_characteristics(const int qfi, rrc_pdu_session_param_t *pduSession) +pdusession_level_qos_parameter_t *get_qos_characteristics(const int qfi, rrc_pdu_session_param_t *pduSession) { pdusession_t *pdu = &pduSession->param; for (int i = 0; i < pdu->nb_qos; i++) { @@ -2035,7 +2035,7 @@ static pdusession_level_qos_parameter_t *get_qos_characteristics(const int qfi, } /* \bref return F1AP QoS characteristics based on Qos flow parameters */ -static f1ap_qos_characteristics_t get_qos_char_from_qos_flow_param(const pdusession_level_qos_parameter_t *qos_param) +f1ap_qos_characteristics_t get_qos_char_from_qos_flow_param(const pdusession_level_qos_parameter_t *qos_param) { f1ap_qos_characteristics_t qos_char = {0}; if (qos_param->fiveQI_type == DYNAMIC) { From 66b548060e8d3740553b68a1662e2d9964d6c0ff Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Fri, 4 Oct 2024 20:03:44 +0200 Subject: [PATCH 26/41] Implement F1 Handover Trigger Add a function to trigger an F1 handover, including the corresponding telnet function implementation. The function looks up source&target DU, creates a HO context, and reads PDU information to send a UE context setup request to the target DU towards which to handover. Co-authored-by: batuhanduyuler Co-authored-by: rmagueta --- openair2/RRC/NR/rrc_gNB_du.c | 28 +++++ openair2/RRC/NR/rrc_gNB_du.h | 2 + openair2/RRC/NR/rrc_gNB_mobility.c | 195 +++++++++++++++++++++++++++++ openair2/RRC/NR/rrc_gNB_mobility.h | 2 + 4 files changed, 227 insertions(+) diff --git a/openair2/RRC/NR/rrc_gNB_du.c b/openair2/RRC/NR/rrc_gNB_du.c index e2806edf60..51abc95dd0 100644 --- a/openair2/RRC/NR/rrc_gNB_du.c +++ b/openair2/RRC/NR/rrc_gNB_du.c @@ -592,3 +592,31 @@ void dump_du_info(const gNB_RRC_INST *rrc, FILE *f) } } } + +nr_rrc_du_container_t *find_target_du(gNB_RRC_INST *rrc, sctp_assoc_t source_assoc_id) +{ + nr_rrc_du_container_t *target_du = NULL; + nr_rrc_du_container_t *it = NULL; + bool next_du = false; + RB_FOREACH (it, rrc_du_tree, &rrc->dus) { + if (next_du == false && source_assoc_id != it->assoc_id) { + continue; + } else if (source_assoc_id == it->assoc_id) { + next_du = true; + } else { + target_du = it; + break; + } + } + if (target_du == NULL) { + RB_FOREACH (it, rrc_du_tree, &rrc->dus) { + if (source_assoc_id == it->assoc_id) { + continue; + } else { + target_du = it; + break; + } + } + } + return target_du; +} diff --git a/openair2/RRC/NR/rrc_gNB_du.h b/openair2/RRC/NR/rrc_gNB_du.h index 2c0b5574ce..55a0d7c2c8 100644 --- a/openair2/RRC/NR/rrc_gNB_du.h +++ b/openair2/RRC/NR/rrc_gNB_du.h @@ -48,6 +48,8 @@ int get_dl_band(const struct f1ap_served_cell_info_t *cell_info); int get_ssb_scs(const struct f1ap_served_cell_info_t *cell_info); int get_ssb_arfcn(const struct nr_rrc_du_container_t *du); +struct nr_rrc_du_container_t *find_target_du(struct gNB_RRC_INST_s *rrc, sctp_assoc_t source_assoc_id); + // the assoc_id might be 0 (if the DU goes offline). Below helper macro to // print an error and return from the function in that case #define RETURN_IF_INVALID_ASSOC_ID(assoc_id) \ diff --git a/openair2/RRC/NR/rrc_gNB_mobility.c b/openair2/RRC/NR/rrc_gNB_mobility.c index 72ae57b67e..cb6e851ebb 100644 --- a/openair2/RRC/NR/rrc_gNB_mobility.c +++ b/openair2/RRC/NR/rrc_gNB_mobility.c @@ -22,8 +22,16 @@ #include #include "assertions.h" + #include "rrc_gNB_mobility.h" +#include "nr_rrc_proto.h" +#include "rrc_gNB_du.h" +#include "rrc_gNB_radio_bearers.h" +#include "rrc_gNB_UE_context.h" +#include "openair2/F1AP/f1ap_ids.h" +#include "MESSAGES/asn1_msg.h" + typedef enum { HO_CTX_BOTH, HO_CTX_SOURCE, HO_CTX_TARGET } ho_ctx_type_t; static nr_handover_context_t *alloc_ho_ctx(ho_ctx_type_t type) { @@ -46,3 +54,190 @@ static void free_ho_ctx(nr_handover_context_t *ho_ctx) free(ho_ctx->target); free(ho_ctx); } + +/* \brief Initiate a handover of UE to a specific target cell handled by this + * CU. + * \param ue a UE context for which the handover should be triggered. The UE + * context must be non-null (if the HO request comes from "outside" (N2, Xn), + * the UE contex must be created first. + * \param target_du the DU towards which to handover. Note: currently, the CU + * is limited to one cell per DU, so DU and cell are equivalent here. + * \param ho_ctxt contextual data for the type of handover (F1, N2, Xn) */ +static void nr_initiate_handover(const gNB_RRC_INST *rrc, + gNB_RRC_UE_t *ue, + const nr_rrc_du_container_t *source_du, + const nr_rrc_du_container_t *target_du, + uint8_t *ho_prep_buf, + uint32_t ho_prep_len, + ho_req_ack_t ack, + ho_success_t success, + ho_cancel_t cancel) +{ + DevAssert(rrc != NULL); + DevAssert(ue != NULL); + DevAssert(target_du != NULL); + // source_du might be NULL -> inter-CU handover + DevAssert(ho_prep_buf != NULL && ho_prep_len > 0); + + if (ue->ho_context != NULL) { + LOG_E(NR_RRC, "handover for UE %u ongoing, cannot trigger new\n", ue->rrc_ue_id); + return; + } + // if any reconfiguration is ongoing, abort handover request + for (int i = 0; i < NR_RRC_TRANSACTION_IDENTIFIER_NUMBER; ++i) { + if (ue->xids[i] != RRC_ACTION_NONE) { + LOG_E(NR_RRC, "UE %d: ongoig transaction %d (action %d), cannot trigger handover\n", ue->rrc_ue_id, i, ue->xids[i]); + return; + } + } + + // if we know the source DU (F1 handover), allocate the context for it, + // otherwise only for the target DU (N2, Xn) + ho_ctx_type_t ctx_type = source_du != NULL ? HO_CTX_BOTH : HO_CTX_TARGET; + nr_handover_context_t *ho_ctx = alloc_ho_ctx(ctx_type); + ho_ctx->target->du = target_du; + // we will know target->{du_ue_id,new_rnti} once we have UE ctxt setup + // response + ho_ctx->target->ho_req_ack = ack; + ho_ctx->target->ho_success = success; + + const f1_ue_data_t ue_data = cu_get_f1_ue_data(ue->rrc_ue_id); + if (source_du != NULL) { + DevAssert(source_du->assoc_id == ue_data.du_assoc_id); + // we also have the source DU (F1 handover), store meta info + ho_ctx->source->ho_cancel = cancel; + + ho_ctx->source->du = source_du; + ho_ctx->source->du_ue_id = ue_data.secondary_ue; + ho_ctx->source->old_rnti = ue->rnti; + } + ue->ho_context = ho_ctx; + LOG_A(NR_RRC, + "Handover triggered for UE %u/RNTI %04x towards DU %ld/assoc_id %d/PCI %d\n", + ue->rrc_ue_id, + ue->rnti, + target_du->setup_req->gNB_DU_id, + target_du->assoc_id, + target_du->setup_req->cell[0].info.nr_pci); + + cu_to_du_rrc_information_t cu2du = { + .handoverPreparationInfo = ho_prep_buf, + .handoverPreparationInfo_length = ho_prep_len, + }; + + f1ap_drb_to_be_setup_t drbs[32] = {0}; // maximum DRB can be 32 + int nb_drb = 0; + for (int i = 0; i < 32; ++i) { /* for each DRB */ + drb_t *rrc_drb = &ue->established_drbs[i]; + if (rrc_drb->status == DRB_INACTIVE) + continue; + + f1ap_drb_to_be_setup_t *drb = &drbs[nb_drb]; + nb_drb++; + drb->drb_id = rrc_drb->drb_id; + drb->rlc_mode = rrc->configuration.um_on_default_drb ? F1AP_RLC_MODE_UM_BIDIR : F1AP_RLC_MODE_AM; + memcpy(&drb->up_ul_tnl[0].tl_address, &rrc_drb->cuup_tunnel_config.addr.buffer, sizeof(uint8_t) * 4); + drb->up_ul_tnl[0].port = rrc->eth_params_s.my_portd; + drb->up_ul_tnl[0].teid = rrc_drb->cuup_tunnel_config.teid; + drb->up_ul_tnl_length = 1; + + /* fetch an existing PDU session for this DRB */ + rrc_pdu_session_param_t *pdu = find_pduSession_from_drbId(ue, drb->drb_id); + AssertFatal(pdu != NULL, "no PDU session for DRB ID %ld\n", drb->drb_id); + drb->nssai = pdu->param.nssai; + + // for the moment, we only support one QoS flow. Put a reminder in case + // this changes + AssertFatal(pdu->param.nb_qos == 1, "only 1 Qos flow supported\n"); + drb->drb_info.flows_to_be_setup_length = 1; + drb->drb_info.flows_mapped_to_drb = calloc(1, sizeof(f1ap_flows_mapped_to_drb_t)); + AssertFatal(drb->drb_info.flows_mapped_to_drb, "could not allocate memory\n"); + int qfi = rrc_drb->cnAssociation.sdap_config.mappedQoS_FlowsToAdd[0]; + DevAssert(qfi > 0); + drb->drb_info.flows_mapped_to_drb[0].qfi = qfi; + pdusession_level_qos_parameter_t *in_qos_char = get_qos_characteristics(qfi, pdu); + drb->drb_info.flows_mapped_to_drb[0].qos_params.qos_characteristics = get_qos_char_from_qos_flow_param(in_qos_char); + + /* the DRB QoS parameters: we just reuse the ones from the first flow */ + drb->drb_info.drb_qos = drb->drb_info.flows_mapped_to_drb[0].qos_params; + } + + f1ap_srb_to_be_setup_t srbs[2] = {{.srb_id = 1, .lcid = 1}, {.srb_id = 2, .lcid = 2}}; + f1ap_served_cell_info_t *cell_info = &target_du->setup_req->cell[0].info; + RETURN_IF_INVALID_ASSOC_ID(target_du->assoc_id); + f1ap_ue_context_setup_t ue_context_setup_req = { + .gNB_CU_ue_id = ue->rrc_ue_id, + .gNB_DU_ue_id = 0, /* TODO: this should be optional! */ + .plmn.mcc = cell_info->plmn.mcc, + .plmn.mnc = cell_info->plmn.mnc, + .plmn.mnc_digit_length = cell_info->plmn.mnc_digit_length, + .nr_cellid = cell_info->nr_cellid, + .servCellId = 0, // TODO: correct value? + .srbs_to_be_setup_length = 2, + .srbs_to_be_setup = srbs, + .drbs_to_be_setup_length = nb_drb, + .drbs_to_be_setup = drbs, + .cu_to_du_rrc_information = &cu2du, + }; + rrc->mac_rrc.ue_context_setup_request(target_du->assoc_id, &ue_context_setup_req); +} + +/* +void nr_rrc_trigger_n2_ho(gNB_RRC_INST *rrc, int nr_cgi, uint8_t *ho_prep_info, uint32_t *ho_prep_len, void *pdu_session) +{ + // call from outside (via NGAP message), with the request for handover for a + // (any) UE. This handler should, in order: + // + // 1. look up the target DU =du= via get_du_by_cell_id(nr_cgi) + // (and reject the request if it can't find the DU) + // 2. create a context for a new UE =UE=, based on pdu_session information + // 3. call nr_initiate_handover(rrc, UE, NULL, du, ho_prep_info, ho_prep_len); + // + // after success of the DU, send the result via =success_ptr= to the source + // CU. +} +*/ + +void nr_rrc_trigger_f1_ho(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, nr_rrc_du_container_t *source_du, nr_rrc_du_container_t *target_du) +{ + DevAssert(rrc != NULL); + DevAssert(ue != NULL); + + uint8_t buf[NR_RRC_BUF_SIZE]; + int size = do_NR_HandoverPreparationInformation(ue->ue_cap_buffer.buf, ue->ue_cap_buffer.len, buf, sizeof buf); + + // corresponds to a "handover request", 38.300 Sec 9.3.2.3 + // see also 38.413 Sec 9.3.1.29 for information on source-CU to target-CU + // information (Source NG-RAN Node to Target NG-RAN Node Transparent Container) + // here: target Cell is preselected, target CU has access to UE information + // and therefore also the PDU sessions. Orig RRC reconfiguration should be in + // handover preparation information + ho_req_ack_t ack = NULL; + ho_success_t success = NULL; + ho_cancel_t cancel = NULL; + nr_initiate_handover(rrc, ue, source_du, target_du, buf, size, ack, success, cancel); +} + +void nr_HO_F1_trigger_telnet(gNB_RRC_INST *rrc, uint32_t rrc_ue_id) +{ + rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(rrc, rrc_ue_id); + if (ue_context_p == NULL) { + LOG_E(NR_RRC, "cannot find UE context for UE ID %d\n", rrc_ue_id); + return; + } + gNB_RRC_UE_t *ue = &ue_context_p->ue_context; + nr_rrc_du_container_t *source_du = get_du_for_ue(rrc, ue->rrc_ue_id); + if (source_du == NULL) { + f1_ue_data_t ue_data = cu_get_f1_ue_data(rrc_ue_id); + LOG_E(NR_RRC, "cannot get source gNB-DU with assoc_id %d for UE %u\n", ue_data.du_assoc_id, ue->rrc_ue_id); + return; + } + + nr_rrc_du_container_t *target_du = find_target_du(rrc, source_du->assoc_id); + if (target_du == NULL) { + LOG_E(NR_RRC, "No target gNB-DU found. Handover for UE %u aborted.\n", ue->rrc_ue_id); + return; + } + + nr_rrc_trigger_f1_ho(rrc, ue, source_du, target_du); +} diff --git a/openair2/RRC/NR/rrc_gNB_mobility.h b/openair2/RRC/NR/rrc_gNB_mobility.h index fefa39982b..5811804009 100644 --- a/openair2/RRC/NR/rrc_gNB_mobility.h +++ b/openair2/RRC/NR/rrc_gNB_mobility.h @@ -66,4 +66,6 @@ typedef struct nr_handover_context_s { nr_ho_target_cu_t *target; } nr_handover_context_t; +void nr_rrc_trigger_f1_ho(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, nr_rrc_du_container_t *source_du, nr_rrc_du_container_t *target_du); + #endif /* RRC_GNB_MOBILITY_H_ */ From 7b163f16889398385f9af1233e20eef7a1bf2f64 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Fri, 4 Oct 2024 21:54:39 +0200 Subject: [PATCH 27/41] Harmonize UE context setup resp bearer handling This is preparatory work to handle the UE context setup response from the target DU. There are two cases: - "Initial" UE context setup response (during connection setup): we need to inform the CU-UP via an E1 message of the new bearers (GTP tunnel update) - F1 handover: we need to inform the CU-UP via an E1 message that there are DRBs to reestablish There are two different functions, e1_send_bearer_updates() and cuup_notify_reestablishment(), to do this. Harmonize the code, before implementing the actual handover logic in the next commit. --- openair2/RRC/NR/rrc_gNB.c | 8 +++++++- openair2/RRC/NR/rrc_gNB_radio_bearers.c | 9 +++++++++ openair2/RRC/NR/rrc_gNB_radio_bearers.h | 3 +++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index 268dfb7772..08547d7542 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -1851,11 +1851,17 @@ static void rrc_CU_process_ue_context_setup_response(MessageDef *msg_p, instance xer_fprint(stdout, &asn_DEF_NR_CellGroupConfig, UE->masterCellGroup); if (resp->drbs_to_be_setup_length > 0) { + int num_drb = get_number_active_drbs(UE); + DevAssert(num_drb == 0 || num_drb == resp->drbs_to_be_setup_length); + /* Note: we would ideally check that SRB2 is acked, but at least LiteOn DU * seems buggy and does not ack, so simply check that locally we activated */ AssertFatal(UE->Srb[1].Active && UE->Srb[2].Active, "SRBs 1 and 2 must be active during DRB Establishment"); store_du_f1u_tunnel(resp->drbs_to_be_setup, resp->drbs_to_be_setup_length, UE); - e1_send_bearer_updates(rrc, UE, resp->drbs_to_be_setup_length, resp->drbs_to_be_setup); + if (num_drb == 0) + e1_send_bearer_updates(rrc, UE, resp->drbs_to_be_setup_length, resp->drbs_to_be_setup); + else + cuup_notify_reestablishment(rrc, UE); } rrc_gNB_generate_dedicatedRRCReconfiguration(rrc, UE); diff --git a/openair2/RRC/NR/rrc_gNB_radio_bearers.c b/openair2/RRC/NR/rrc_gNB_radio_bearers.c index ab2bf13071..aec7865a1c 100644 --- a/openair2/RRC/NR/rrc_gNB_radio_bearers.c +++ b/openair2/RRC/NR/rrc_gNB_radio_bearers.c @@ -181,6 +181,15 @@ uint8_t get_next_available_drb_id(gNB_RRC_UE_t *ue) return DRB_INACTIVE; } +int get_number_active_drbs(gNB_RRC_UE_t *ue) +{ + int n = 0; + for (int i = 0; i < MAX_DRBS_PER_UE; ++i) + if (ue->established_drbs[i].status != DRB_INACTIVE) + n++; + return n; +} + bool drb_is_active(gNB_RRC_UE_t *ue, uint8_t drb_id) { drb_t *drb = get_drb(ue, drb_id); diff --git a/openair2/RRC/NR/rrc_gNB_radio_bearers.h b/openair2/RRC/NR/rrc_gNB_radio_bearers.h index 82502d8669..29fead6bea 100644 --- a/openair2/RRC/NR/rrc_gNB_radio_bearers.h +++ b/openair2/RRC/NR/rrc_gNB_radio_bearers.h @@ -61,6 +61,9 @@ drb_t *generateDRB(gNB_RRC_UE_t *ue, /// @brief return the next available (inactive) DRB ID of UE ue uint8_t get_next_available_drb_id(gNB_RRC_UE_t *ue); +/// @brief returns the number of active DRBs for this UE +int get_number_active_drbs(gNB_RRC_UE_t *ue); + /// @brief check if DRB with ID drb_id of UE ue is active bool drb_is_active(gNB_RRC_UE_t *ue, uint8_t drb_id); From 0e20c4c25735c36f609251233cf4244bdaf9478a Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Fri, 4 Oct 2024 22:00:06 +0200 Subject: [PATCH 28/41] Handle UE context setup resp for HO: trigger reconfiguration on source In this commit, handle the reception of a UE context setup response, in the case of handover. In that case, store the gNB-DU UE ID and C-RNTI of the UE at the target DU, and trigger a reconfiguration towards the UE at the source DU via the callback. To ensure that the CU-UP's PDCP is SN-aligned with the UE, use cuup_notify_reestablishment() to reset the bearers with PDCP reestablishment, which has already been implemented in the last commit. Co-authored-by: batuhanduyuler Co-authored-by: rmagueta --- openair2/RRC/NR/rrc_gNB.c | 22 ++++++++- openair2/RRC/NR/rrc_gNB_mobility.c | 76 +++++++++++++++++++++++++++++- 2 files changed, 96 insertions(+), 2 deletions(-) diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index 08547d7542..e0c94fc293 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -1864,7 +1864,27 @@ static void rrc_CU_process_ue_context_setup_response(MessageDef *msg_p, instance cuup_notify_reestablishment(rrc, UE); } - rrc_gNB_generate_dedicatedRRCReconfiguration(rrc, UE); + if (UE->ho_context == NULL) { + rrc_gNB_generate_dedicatedRRCReconfiguration(rrc, UE); + } else { + // case of handover + // handling of "target CU" information + DevAssert(UE->ho_context->target != NULL); + DevAssert(resp->crnti != NULL); + UE->ho_context->target->du_ue_id = resp->gNB_DU_ue_id; + UE->ho_context->target->new_rnti = *resp->crnti; + + uint8_t xid = rrc_gNB_get_next_transaction_identifier(0); + UE->xids[xid] = RRC_DEDICATED_RECONF; + uint8_t buffer[NR_RRC_BUF_SIZE] = {0}; + int size = rrc_gNB_encode_RRCReconfiguration(rrc, UE, xid, NULL, buffer, sizeof(buffer), true); + DevAssert(size > 0 && size <= sizeof(buffer)); + + // TODO N2 38.413 sec 9.3.1.21: admitted PDU sessions (in UE), Target to + // Source Transparent Container (9.3.1.21) which encodes the RRC + // reconfiguration above + UE->ho_context->target->ho_req_ack(rrc, UE, buffer, size); + } } static void rrc_CU_process_ue_context_release_request(MessageDef *msg_p) diff --git a/openair2/RRC/NR/rrc_gNB_mobility.c b/openair2/RRC/NR/rrc_gNB_mobility.c index cb6e851ebb..c423e400f4 100644 --- a/openair2/RRC/NR/rrc_gNB_mobility.c +++ b/openair2/RRC/NR/rrc_gNB_mobility.c @@ -31,6 +31,11 @@ #include "rrc_gNB_UE_context.h" #include "openair2/F1AP/f1ap_ids.h" #include "MESSAGES/asn1_msg.h" +#include "nr_pdcp/nr_pdcp_oai_api.h" +#include "openair3/SECU/key_nas_deriver.h" + +/* declared in rrc_gNB.c: should be cleaned up through helper function? useful? */ +extern mui_t rrc_gNB_mui; typedef enum { HO_CTX_BOTH, HO_CTX_SOURCE, HO_CTX_TARGET } ho_ctx_type_t; static nr_handover_context_t *alloc_ho_ctx(ho_ctx_type_t type) @@ -198,6 +203,75 @@ void nr_rrc_trigger_n2_ho(gNB_RRC_INST *rrc, int nr_cgi, uint8_t *ho_prep_info, } */ +typedef struct deliver_ue_ctxt_modification_data_t { + gNB_RRC_INST *rrc; + f1ap_ue_context_modif_req_t *modification_req; + sctp_assoc_t assoc_id; +} deliver_ue_ctxt_modification_data_t; +static void rrc_deliver_ue_ctxt_modif_req(void *deliver_pdu_data, ue_id_t ue_id, int srb_id, char *buf, int size, int sdu_id) +{ + DevAssert(deliver_pdu_data != NULL); + deliver_ue_ctxt_modification_data_t *data = deliver_pdu_data; + data->modification_req->rrc_container = (uint8_t*)buf; + data->modification_req->rrc_container_length = size; + data->rrc->mac_rrc.ue_context_modification_request(data->assoc_id, data->modification_req); +} +static void rrc_gNB_trigger_reconfiguration_for_handover(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, uint8_t *rrc_reconf, int rrc_reconf_len) +{ + f1_ue_data_t ue_data = cu_get_f1_ue_data(ue->rrc_ue_id); + + TransmActionInd_t transmission_action_indicator = TransmActionInd_STOP; + RETURN_IF_INVALID_ASSOC_ID(ue_data.du_assoc_id); + f1ap_ue_context_modif_req_t ue_context_modif_req = { + .gNB_CU_ue_id = ue->rrc_ue_id, + .gNB_DU_ue_id = ue_data.secondary_ue, + .plmn.mcc = rrc->configuration.mcc[0], + .plmn.mnc = rrc->configuration.mnc[0], + .plmn.mnc_digit_length = rrc->configuration.mnc_digit_length[0], + .nr_cellid = rrc->nr_cellid, // TODO target cell ID + .servCellId = 0, // TODO: correct value? + .ReconfigComplOutcome = RRCreconf_success, + .transm_action_ind = &transmission_action_indicator, + }; + deliver_ue_ctxt_modification_data_t data = {.rrc = rrc, + .modification_req = &ue_context_modif_req, + .assoc_id = ue_data.du_assoc_id}; + int srb_id = 1; + nr_pdcp_data_req_srb(ue->rrc_ue_id, + srb_id, + rrc_gNB_mui++, + rrc_reconf_len, + (unsigned char *const)rrc_reconf, + rrc_deliver_ue_ctxt_modif_req, + &data); +} + +static void nr_rrc_f1_ho_acknowledge(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, uint8_t *rrc_reconf_buf, uint32_t rrc_reconf_len) +{ + // N2/Xn HO: fill with UE caps, as-context, rrc reconf, send to source CU + // also, fill the UE->rnti from the new one (in F1 case, happens after + // confirmation of ue ctxt modif + + // F1 HO: handling of "source CU" information + DevAssert(UE->ho_context->source != NULL); + rrc_gNB_trigger_reconfiguration_for_handover(rrc, UE, rrc_reconf_buf, rrc_reconf_len); + LOG_A(NR_RRC, "HO acknowledged: Send reconfiguration for UE %u/RNTI %04x...\n", UE->rrc_ue_id, UE->rnti); + + /* Re-establish SRB2 according to clause 5.3.5.6.3 of 3GPP TS 38.331 + * (SRB1 is re-established with RRCReestablishment message) + */ + int srb_id = 2; + if (UE->Srb[srb_id].Active) { + nr_pdcp_entity_security_keys_and_algos_t security_parameters; + /* Derive the keys from kgnb */ + nr_derive_key(RRC_ENC_ALG, UE->ciphering_algorithm, UE->kgnb, security_parameters.ciphering_key); + nr_derive_key(RRC_INT_ALG, UE->integrity_algorithm, UE->kgnb, security_parameters.integrity_key); + security_parameters.integrity_algorithm = UE->integrity_algorithm; + security_parameters.ciphering_algorithm = UE->ciphering_algorithm; + nr_pdcp_reestablishment(UE->rrc_ue_id, srb_id, true, &security_parameters); + } +} + void nr_rrc_trigger_f1_ho(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, nr_rrc_du_container_t *source_du, nr_rrc_du_container_t *target_du) { DevAssert(rrc != NULL); @@ -212,7 +286,7 @@ void nr_rrc_trigger_f1_ho(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, nr_rrc_du_contain // here: target Cell is preselected, target CU has access to UE information // and therefore also the PDU sessions. Orig RRC reconfiguration should be in // handover preparation information - ho_req_ack_t ack = NULL; + ho_req_ack_t ack = nr_rrc_f1_ho_acknowledge; ho_success_t success = NULL; ho_cancel_t cancel = NULL; nr_initiate_handover(rrc, ue, source_du, target_du, buf, size, ack, success, cancel); From e42c303bd72006262a60a50c887ada401aaed810 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Fri, 31 May 2024 19:49:43 +0200 Subject: [PATCH 29/41] Handle UE Context Modification Response in HO case We expect the UE's RRC Reconfiguration Complete to come through the target DU, as the "new UE" at the target CU. Also, function CU_handle_UL_RRC_MESSAGE_TRANSFER() checks that the UL RRC Message, through which we will receive RRC Reconfiguration Complete, matches the DU UE ID stored in RRC. Since the DU UE ID will be the one at the target DU, while up to now, we stored the DU UE ID at the source DU. Correspondingly, update the DU UE ID to the one at the target DU; also update the corresponding assoc_id. Co-authored-by: batuhanduyuler Co-authored-by: rmagueta --- openair2/RRC/NR/rrc_gNB.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index e0c94fc293..3335116a21 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -1983,6 +1983,24 @@ static void rrc_CU_process_ue_context_modification_response(MessageDef *msg_p, i rrc_gNB_generate_dedicatedRRCReconfiguration(rrc, UE); } + + // Reconfiguration should have been sent to the UE, so it will attempt the + // handover. In the F1 case, update with new RNTI, and update secondary UE + // association, so we can receive the new UE from the target DU (in N2/Xn, + // nothing is to be done, we wait for confirmation to release the UE in the + // CU/DU) + if (UE->ho_context && UE->ho_context->target && UE->ho_context->source) { + nr_ho_target_cu_t *target_ctx = UE->ho_context->target; + f1_ue_data_t ue_data = cu_get_f1_ue_data(UE->rrc_ue_id); + ue_data.secondary_ue = target_ctx->du_ue_id; + ue_data.du_assoc_id = target_ctx->du->assoc_id; + cu_remove_f1_ue_data(UE->rrc_ue_id); + cu_add_f1_ue_data(UE->rrc_ue_id, &ue_data); + LOG_I(NR_RRC, "UE %d handover: update RNTI from %04x to %04x\n", UE->rrc_ue_id, UE->rnti, target_ctx->new_rnti); + nr_ho_source_cu_t *source_ctx = UE->ho_context->source; + DevAssert(source_ctx->old_rnti == UE->rnti); + UE->rnti = target_ctx->new_rnti; + } } static void rrc_CU_process_ue_modification_required(MessageDef *msg_p, instance_t instance) From a2da391e70034ea22aac0eb3e8ceb2d459ca7629 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Fri, 4 Oct 2024 19:46:00 +0200 Subject: [PATCH 30/41] Handle successful HO after reconfigurationComplete The HO is completed (from a pure RAN perspective) when receiving reconfiguration complete. In that case, remove the HO context, and signal successful handover through the callback. For the F1 handover, this means releasing the UE at the source DU. Co-authored-by: batuhanduyuler Co-authored-by: rmagueta --- openair2/RRC/NR/rrc_gNB.c | 8 ++++++++ openair2/RRC/NR/rrc_gNB_mobility.c | 26 +++++++++++++++++++++++++- openair2/RRC/NR/rrc_gNB_mobility.h | 1 + 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index 3335116a21..0ab53e0bf7 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -1558,6 +1558,14 @@ static void handle_rrcReconfigurationComplete(gNB_RRC_INST *rrc, gNB_RRC_UE_t *U LOG_I(RRC, "UE %d: transaction %d still ongoing for action %d\n", UE->rrc_ue_id, i, UE->xids[i]); } } + + if (UE->ho_context != NULL) { + LOG_A(NR_RRC, "handover for UE %d/RNTI %04x complete!\n", UE->rrc_ue_id, UE->rnti); + DevAssert(UE->ho_context->target != NULL); + + UE->ho_context->target->ho_success(rrc, UE); + nr_rrc_finalize_ho(UE); + } } static void rrc_gNB_generate_UECapabilityEnquiry(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue) diff --git a/openair2/RRC/NR/rrc_gNB_mobility.c b/openair2/RRC/NR/rrc_gNB_mobility.c index c423e400f4..6f6a034aa3 100644 --- a/openair2/RRC/NR/rrc_gNB_mobility.c +++ b/openair2/RRC/NR/rrc_gNB_mobility.c @@ -272,6 +272,24 @@ static void nr_rrc_f1_ho_acknowledge(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, uint8_ } } +static void nr_rrc_f1_ho_complete(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE) +{ + DevAssert(UE->ho_context != NULL); + nr_ho_source_cu_t *source_ctx = UE->ho_context->source; + DevAssert(source_ctx != NULL); + RETURN_IF_INVALID_ASSOC_ID(source_ctx->du->assoc_id); + f1ap_ue_context_release_cmd_t cmd = { + .gNB_CU_ue_id = UE->rrc_ue_id, + .gNB_DU_ue_id = source_ctx->du_ue_id, + .cause = F1AP_CAUSE_RADIO_NETWORK, // better + .cause_value = 5, // 5 = F1AP_CauseRadioNetwork_interaction_with_other_procedure + .srb_id = DCCH, + }; + rrc->mac_rrc.ue_context_release_command(source_ctx->du->assoc_id, &cmd); + LOG_I(NR_RRC, "UE %d Handover: trigger release on DU assoc_id %d\n", UE->rrc_ue_id, source_ctx->du->assoc_id); +} + + void nr_rrc_trigger_f1_ho(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, nr_rrc_du_container_t *source_du, nr_rrc_du_container_t *target_du) { DevAssert(rrc != NULL); @@ -287,11 +305,17 @@ void nr_rrc_trigger_f1_ho(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, nr_rrc_du_contain // and therefore also the PDU sessions. Orig RRC reconfiguration should be in // handover preparation information ho_req_ack_t ack = nr_rrc_f1_ho_acknowledge; - ho_success_t success = NULL; + ho_success_t success = nr_rrc_f1_ho_complete; ho_cancel_t cancel = NULL; nr_initiate_handover(rrc, ue, source_du, target_du, buf, size, ack, success, cancel); } +void nr_rrc_finalize_ho(gNB_RRC_UE_t *ue) +{ + free_ho_ctx(ue->ho_context); + ue->ho_context = NULL; +} + void nr_HO_F1_trigger_telnet(gNB_RRC_INST *rrc, uint32_t rrc_ue_id) { rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(rrc, rrc_ue_id); diff --git a/openair2/RRC/NR/rrc_gNB_mobility.h b/openair2/RRC/NR/rrc_gNB_mobility.h index 5811804009..1078cffe4e 100644 --- a/openair2/RRC/NR/rrc_gNB_mobility.h +++ b/openair2/RRC/NR/rrc_gNB_mobility.h @@ -67,5 +67,6 @@ typedef struct nr_handover_context_s { } nr_handover_context_t; void nr_rrc_trigger_f1_ho(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, nr_rrc_du_container_t *source_du, nr_rrc_du_container_t *target_du); +void nr_rrc_finalize_ho(gNB_RRC_UE_t *ue); #endif /* RRC_GNB_MOBILITY_H_ */ From 145fd2b41552ac411a5bb062b3d940ba47e07fbc Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 14 Aug 2024 15:36:52 +0200 Subject: [PATCH 31/41] Handle Release request during handover If we release a request from a DU during, handle as follows: - from the source: Handover is still ongoing, so ignore it by acknowledging the release, and marking handover as done. In this case, we did not receive an acknowledgement from the target, but since the source DU does not keep the UE context, we could not cancel the handover anymore either. The UE might come back on any DU through reestablishment (or simply be connected on the target) - from the target: consider this to be a genuine release, and forward to AMF. Co-authored-by: batuhanduyuler Co-authored-by: rmagueta --- openair2/RRC/NR/rrc_gNB.c | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index 0ab53e0bf7..ea962d7461 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -1895,7 +1895,7 @@ static void rrc_CU_process_ue_context_setup_response(MessageDef *msg_p, instance } } -static void rrc_CU_process_ue_context_release_request(MessageDef *msg_p) +static void rrc_CU_process_ue_context_release_request(MessageDef *msg_p, sctp_assoc_t assoc_id) { const int instance = 0; f1ap_ue_context_release_req_t *req = &F1AP_UE_CONTEXT_RELEASE_REQ(msg_p); @@ -1906,6 +1906,31 @@ static void rrc_CU_process_ue_context_release_request(MessageDef *msg_p) return; } + gNB_RRC_UE_t *UE = &ue_context_p->ue_context; + if (UE->ho_context != NULL) { + nr_ho_source_cu_t *source_ctx = UE->ho_context->source; + bool from_source_du = source_ctx && source_ctx->du->assoc_id == assoc_id; + if (from_source_du) { + // we received release request from the source DU, but HO is still + // ongoing; free the UE, and remove the HO context. + LOG_W(NR_RRC, "UE %d: release request from source DU ID %ld during HO, marking HO as complete\n", UE->rrc_ue_id, source_ctx->du->setup_req->gNB_DU_id); + RETURN_IF_INVALID_ASSOC_ID(source_ctx->du->assoc_id); + f1ap_ue_context_release_cmd_t cmd = { + .gNB_CU_ue_id = UE->rrc_ue_id, + .gNB_DU_ue_id = source_ctx->du_ue_id, + .cause = F1AP_CAUSE_RADIO_NETWORK, + .cause_value = 5, // 5 = F1AP_CauseRadioNetwork_interaction_with_other_procedure + .srb_id = DCCH, + }; + rrc->mac_rrc.ue_context_release_command(assoc_id, &cmd); + nr_rrc_finalize_ho(UE); + return; + } + // if we receive the release request from the target DU (regardless if + // successful), we assume it is "genuine" and ask the AMF to release + nr_rrc_finalize_ho(UE); + } + /* TODO: marshall types correctly */ LOG_I(NR_RRC, "received UE Context Release Request for UE %u, forwarding to AMF\n", req->gNB_CU_ue_id); rrc_gNB_send_NGAP_UE_CONTEXT_RELEASE_REQ(instance, @@ -2430,7 +2455,7 @@ void *rrc_gnb_task(void *args_p) { break; case F1AP_UE_CONTEXT_RELEASE_REQ: - rrc_CU_process_ue_context_release_request(msg_p); + rrc_CU_process_ue_context_release_request(msg_p, msg_p->ittiMsgHeader.originInstance); break; case F1AP_UE_CONTEXT_RELEASE_COMPLETE: From c6434c80fd12239866e784f1cfe0a05c3a7e557c Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Fri, 25 Oct 2024 14:42:56 +0200 Subject: [PATCH 32/41] Reestablishment hack: free reconfigurationWithSync After handover, the last CellGroupConfig received is the one for handover, containing a reconfigurationWithSync. Resending this CellGroupConfig to the UE after reestablishment would trigger a handover. Free the reconfigurationWithSync to have the UE complete reestablishment at the current cell. The CU implementation of reestablishment modifies the CellGroupConfig. It should not do this, but does as of now. The specification (38.473 Sec 8.7) foresees an optional F1 UE context modification procedure (which can be used to receive the CellGroupConfig). This should be done instead to retrieve the correct CellGroupConfig. Note that the F1 UE context setup procedure, similar to the UE context modification procedure, would also allow to perform reestablishment on a DU that is not the original one. --- openair2/RRC/NR/rrc_gNB.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index ea962d7461..04e2a3d02f 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -909,6 +909,17 @@ static void rrc_gNB_process_RRCReestablishmentComplete(gNB_RRC_INST *rrc, gNB_RR NR_RLC_BearerConfig__reestablishRLC_true); } + /* TODO: remove a reconfigurationWithSync, we don't need it for + * reestablishment. The whole reason why this might be here is that we store + * the CellGroupConfig (after handover), and simply reuse it for + * reestablishment, instead of re-requesting the CellGroupConfig from the DU. + * Hence, add below hack; the solution would be to request the + * CellGroupConfig from the DU when doing reestablishment. */ + if (cellGroupConfig->spCellConfig->reconfigurationWithSync != NULL) { + ASN_STRUCT_FREE(asn_DEF_NR_ReconfigurationWithSync, cellGroupConfig->spCellConfig->reconfigurationWithSync); + cellGroupConfig->spCellConfig->reconfigurationWithSync = NULL; + } + /* Re-establish SRB2 according to clause 5.3.5.6.3 of 3GPP TS 38.331 * (SRB1 is re-established with RRCReestablishment message) */ From b6ccf8aa7773359b5182abf042b354ceaf500f55 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 7 Aug 2024 17:00:38 +0200 Subject: [PATCH 33/41] Handle reestablishment during handover This commit implements reestablishment during handover when occurring at the source DU, either as a handover Failure or "normal" reestablishment (something else went wrong). In this case, the CU will trigger HO cancel, which in F1, means to cancel on the target DU. Also, undo any previous ID changes. Retain the old CellGroupConfig (before handover) for the case of reestablishment at source. If not needed, it is freed after the reconfiguration complete. Reestablishment on target cell during handover cannot be handled by the gNB (yet). It is the same as with a normal reestablishment (not during handover) on a different cell: that cell has no notion of the old UE, so we cannot simply indicate the old UE to reestablish; at least, we would need to cleanup the CellGroupConfig, and I am not sure this works out of the box, either. --- openair2/RRC/NR/rrc_gNB.c | 61 ++++++++++++++++++++++++++++-- openair2/RRC/NR/rrc_gNB_mobility.c | 21 +++++++++- openair2/RRC/NR/rrc_gNB_mobility.h | 5 +++ 3 files changed, 82 insertions(+), 5 deletions(-) diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index 04e2a3d02f..6c642cb582 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -1072,6 +1072,20 @@ static const char *get_reestab_cause(NR_ReestablishmentCause_t c) return "UNKNOWN Failure (ASN.1 decoder error?)"; } +static rrc_gNB_ue_context_t *rrc_gNB_get_ue_context_source_cell(gNB_RRC_INST *rrc_instance_pP, long pci, rnti_t rntiP) +{ + rrc_gNB_ue_context_t *ue_context_p; + RB_FOREACH(ue_context_p, rrc_nr_ue_tree_s, &rrc_instance_pP->rrc_ue_head) { + gNB_RRC_UE_t *ue = &ue_context_p->ue_context; + if (!ue->ho_context || !ue->ho_context->source) + continue; + nr_ho_source_cu_t *source_ctx = ue->ho_context->source; + if (source_ctx->old_rnti == rntiP && source_ctx->du->setup_req->cell[0].info.nr_pci == pci) + return ue_context_p; + } + return NULL; +} + static void rrc_handle_RRCReestablishmentRequest(gNB_RRC_INST *rrc, sctp_assoc_t assoc_id, const NR_RRCReestablishmentRequest_IEs_t *req, @@ -1082,7 +1096,12 @@ static void rrc_handle_RRCReestablishmentRequest(gNB_RRC_INST *rrc, const long physCellId = req->ue_Identity.physCellId; long ngap_cause = NGAP_CAUSE_RADIO_NETWORK_UNSPECIFIED; /* cause in case of NGAP release req */ rrc_gNB_ue_context_t *ue_context_p = NULL; - LOG_I(NR_RRC, "UE %04x physCellId %ld NR_RRCReestablishmentRequest cause %s\n", msg->crnti, physCellId, scause); + LOG_I(NR_RRC, + "Reestablishment RNTI %04x req C-RNTI %04lx physCellId %ld cause %s\n", + msg->crnti, + req->ue_Identity.c_RNTI, + physCellId, + scause); const nr_rrc_du_container_t *du = get_du_by_assoc_id(rrc, assoc_id); if (du == NULL) { @@ -1117,13 +1136,47 @@ static void rrc_handle_RRCReestablishmentRequest(gNB_RRC_INST *rrc, rnti_t old_rnti = req->ue_Identity.c_RNTI; ue_context_p = rrc_gNB_get_ue_context_by_rnti(rrc, assoc_id, old_rnti); if (ue_context_p == NULL) { - LOG_E(NR_RRC, "NR_RRCReestablishmentRequest without UE context, fallback to RRC setup\n"); - goto fallback_rrc_setup; + ue_context_p = rrc_gNB_get_ue_context_source_cell(rrc, physCellId, old_rnti); + if (ue_context_p == NULL) { + LOG_E(NR_RRC, "NR_RRCReestablishmentRequest without UE context, fallback to RRC setup\n"); + goto fallback_rrc_setup; + } } gNB_RRC_UE_t *UE = &ue_context_p->ue_context; + /* should check phys cell ID to identify the correct cell */ const f1ap_served_cell_info_t *cell_info = &du->setup_req->cell[0].info; - if (physCellId != cell_info->nr_pci) { + const f1ap_served_cell_info_t *previous_cell_info = get_cell_information_by_phycellId(physCellId); + + nr_ho_source_cu_t *source_ctx = UE->ho_context ? UE->ho_context->source : NULL; + DevAssert(!source_ctx || source_ctx->du->setup_req->num_cells_available == 1); + bool ho_reestab_on_source = source_ctx && previous_cell_info->nr_cellid == source_ctx->du->setup_req->cell[0].info.nr_cellid; + + if (ho_reestab_on_source) { + /* the UE came back on the source DU while doing handover, release at + * target DU and and update the association to the initial DU one */ + LOG_W(NR_RRC, "handover for UE %d/RNTI %04x failed, rollback to original cell\n", UE->rrc_ue_id, UE->rnti); + + // find the transaction of handover (the corresponding reconfig) and abort it + for (int i = 0; i < NR_RRC_TRANSACTION_IDENTIFIER_NUMBER; ++i) { + if (UE->xids[i] == RRC_DEDICATED_RECONF) + UE->xids[i] = RRC_ACTION_NONE; + } + + source_ctx->ho_cancel(rrc, UE); + + /* we need the original CellGroupConfig */ + ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, UE->masterCellGroup); + UE->masterCellGroup = source_ctx->old_cellGroupConfig; + source_ctx->old_cellGroupConfig = NULL; + + /* update to old DU assoc id -- RNTI + secondary DU UE ID further below */ + f1_ue_data_t ue_data = cu_get_f1_ue_data(UE->rrc_ue_id); + ue_data.du_assoc_id = source_ctx->du->assoc_id; + cu_remove_f1_ue_data(UE->rrc_ue_id); + cu_add_f1_ue_data(UE->rrc_ue_id, &ue_data); + nr_rrc_finalize_ho(UE); + } else if (physCellId != cell_info->nr_pci) { /* UE was moving from previous cell so quickly that RRCReestablishment for previous cell was received in this cell */ LOG_I(NR_RRC, "RRC Reestablishment Request from different physCellId (%ld) than current physCellId (%d), fallback to RRC setup\n", diff --git a/openair2/RRC/NR/rrc_gNB_mobility.c b/openair2/RRC/NR/rrc_gNB_mobility.c index 6f6a034aa3..137e7010c2 100644 --- a/openair2/RRC/NR/rrc_gNB_mobility.c +++ b/openair2/RRC/NR/rrc_gNB_mobility.c @@ -115,6 +115,9 @@ static void nr_initiate_handover(const gNB_RRC_INST *rrc, ho_ctx->source->du = source_du; ho_ctx->source->du_ue_id = ue_data.secondary_ue; ho_ctx->source->old_rnti = ue->rnti; + + int result = asn_copy(&asn_DEF_NR_CellGroupConfig, (void **)&ho_ctx->source->old_cellGroupConfig, ue->masterCellGroup); + AssertFatal(result == 0, "error during asn_copy() of CellGroupConfig\n"); } ue->ho_context = ho_ctx; LOG_A(NR_RRC, @@ -289,6 +292,20 @@ static void nr_rrc_f1_ho_complete(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE) LOG_I(NR_RRC, "UE %d Handover: trigger release on DU assoc_id %d\n", UE->rrc_ue_id, source_ctx->du->assoc_id); } +static void nr_rrc_cancel_f1_ho(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE) +{ + DevAssert(UE->ho_context != NULL); + nr_ho_target_cu_t *target_ctx = UE->ho_context->target; + DevAssert(target_ctx != NULL); + f1ap_ue_context_release_cmd_t cmd = { + .gNB_CU_ue_id = UE->rrc_ue_id, + .gNB_DU_ue_id = target_ctx->du_ue_id, + .cause = F1AP_CAUSE_RADIO_NETWORK, // better + .cause_value = 5, // 5 = F1AP_CauseRadioNetwork_interaction_with_other_procedure + .srb_id = DCCH, + }; + rrc->mac_rrc.ue_context_release_command(target_ctx->du->assoc_id, &cmd); +} void nr_rrc_trigger_f1_ho(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, nr_rrc_du_container_t *source_du, nr_rrc_du_container_t *target_du) { @@ -306,12 +323,14 @@ void nr_rrc_trigger_f1_ho(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, nr_rrc_du_contain // handover preparation information ho_req_ack_t ack = nr_rrc_f1_ho_acknowledge; ho_success_t success = nr_rrc_f1_ho_complete; - ho_cancel_t cancel = NULL; + ho_cancel_t cancel = nr_rrc_cancel_f1_ho; nr_initiate_handover(rrc, ue, source_du, target_du, buf, size, ack, success, cancel); } void nr_rrc_finalize_ho(gNB_RRC_UE_t *ue) { + if (ue->ho_context->source) + ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, ue->ho_context->source->old_cellGroupConfig); free_ho_ctx(ue->ho_context); ue->ho_context = NULL; } diff --git a/openair2/RRC/NR/rrc_gNB_mobility.h b/openair2/RRC/NR/rrc_gNB_mobility.h index 1078cffe4e..5046176c1d 100644 --- a/openair2/RRC/NR/rrc_gNB_mobility.h +++ b/openair2/RRC/NR/rrc_gNB_mobility.h @@ -28,6 +28,8 @@ typedef struct gNB_RRC_INST_s gNB_RRC_INST; typedef struct gNB_RRC_UE_s gNB_RRC_UE_t; typedef struct nr_rrc_du_container_t nr_rrc_du_container_t; +typedef struct NR_CellGroupConfig NR_CellGroupConfig_t; + typedef void (*ho_cancel_t)(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue); typedef struct nr_ho_source_cu { /// pointer to the (source) DU structure @@ -38,6 +40,9 @@ typedef struct nr_ho_source_cu { uint32_t du_ue_id; /// old (source) RNTI (to recognize a UE during reestablishment) rnti_t old_rnti; + /// old (source) CellGroupConfig (to send the cellGroupConfig in case of + /// reestablishment) + NR_CellGroupConfig_t *old_cellGroupConfig; /// function pointer to announce the handover cancellation, e.g., /// reestablishment ho_cancel_t ho_cancel; From 546ffee56bab5b0fc0c87dc4f1bd11aa8739d20e Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Fri, 9 Aug 2024 14:57:21 +0200 Subject: [PATCH 34/41] DU dies during HO: free HO context of UE to avoid dangling data --- openair2/RRC/NR/rrc_gNB_du.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/openair2/RRC/NR/rrc_gNB_du.c b/openair2/RRC/NR/rrc_gNB_du.c index 51abc95dd0..bc2ade3253 100644 --- a/openair2/RRC/NR/rrc_gNB_du.c +++ b/openair2/RRC/NR/rrc_gNB_du.c @@ -24,6 +24,7 @@ #include "common/ran_context.h" #include "nr_rrc_defs.h" #include "rrc_gNB_UE_context.h" +#include "rrc_gNB_mobility.h" #include "openair2/F1AP/f1ap_common.h" #include "openair2/F1AP/f1ap_ids.h" #include "executables/softmodem-common.h" @@ -397,7 +398,12 @@ static int invalidate_du_connections(gNB_RRC_INST *rrc, sctp_assoc_t assoc_id) int count = 0; rrc_gNB_ue_context_t *ue_context_p = NULL; RB_FOREACH(ue_context_p, rrc_nr_ue_tree_s, &rrc->rrc_ue_head) { - uint32_t ue_id = ue_context_p->ue_context.rrc_ue_id; + gNB_RRC_UE_t *UE = &ue_context_p->ue_context; + uint32_t ue_id = UE->rrc_ue_id; + if (UE->ho_context != NULL) { + LOG_W(NR_RRC, "DU disconnected while handover for UE %d active\n", ue_id); + nr_rrc_finalize_ho(UE); + } f1_ue_data_t ue_data = cu_get_f1_ue_data(ue_id); if (ue_data.du_assoc_id == assoc_id) { /* this UE belongs to the DU that disconnected, set du_assoc_id to 0, From 8b4259c047b715769ffbee1bf0f2151379c11c97 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 22 Oct 2024 13:49:31 +0200 Subject: [PATCH 35/41] Ignore UE Context Modification Required during handover During handover, we might receive a UE context Modification required message. Currently, we cannot handle this; ignore the message. --- openair2/RRC/NR/rrc_gNB.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index 6c642cb582..6e0906b63c 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -2100,7 +2100,7 @@ static void rrc_CU_process_ue_context_modification_response(MessageDef *msg_p, i } } -static void rrc_CU_process_ue_modification_required(MessageDef *msg_p, instance_t instance) +static void rrc_CU_process_ue_modification_required(MessageDef *msg_p, instance_t instance, sctp_assoc_t assoc_id) { gNB_RRC_INST *rrc = RC.nrrrc[instance]; f1ap_ue_context_modif_required_t *required = &F1AP_UE_CONTEXT_MODIFICATION_REQUIRED(msg_p); @@ -2117,6 +2117,12 @@ static void rrc_CU_process_ue_modification_required(MessageDef *msg_p, instance_ return; } + gNB_RRC_UE_t *UE = &ue_context_p->ue_context; + if (UE->ho_context && UE->ho_context->source && UE->ho_context->source->du && UE->ho_context->source->du->assoc_id == assoc_id) { + LOG_W(NR_RRC, "UE %d: UE Context Modification Required during handover, ignoring message\n", UE->rrc_ue_id); + return; + } + if (required->du_to_cu_rrc_information && required->du_to_cu_rrc_information->cellGroupConfig) { gNB_RRC_UE_t *UE = &ue_context_p->ue_context; LOG_I(RRC, @@ -2515,7 +2521,7 @@ void *rrc_gnb_task(void *args_p) { break; case F1AP_UE_CONTEXT_MODIFICATION_REQUIRED: - rrc_CU_process_ue_modification_required(msg_p, instance); + rrc_CU_process_ue_modification_required(msg_p, instance, msg_p->ittiMsgHeader.originInstance); break; case F1AP_UE_CONTEXT_RELEASE_REQ: From c956de6f27368aba45ececd8ab1c9581051740e2 Mon Sep 17 00:00:00 2001 From: Roberto Magueta Date: Tue, 3 Sep 2024 10:53:16 +0200 Subject: [PATCH 36/41] Trigger F1 Handover on A3 event When receiving a measurement report including an A3 event, trigger handover towards the indicated cell. Note that currently, we are still limited to one Cell by DU, hence looking up a cell comes down to looking up a DU, as is done here. Co-authored-by: batuhanduyuler Co-authored-by: Luis Pereira Co-authored-by: Robert Schmidt --- openair2/RRC/NR/rrc_gNB.c | 29 ++++++++++++++++++----------- openair2/RRC/NR/rrc_gNB_du.c | 12 ++++++++++++ openair2/RRC/NR/rrc_gNB_du.h | 1 + 3 files changed, 31 insertions(+), 11 deletions(-) diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index 6e0906b63c..0f9fce815b 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -1236,7 +1236,7 @@ static void process_Periodical_Measurement_Report(gNB_RRC_UE_t *ue_ctxt, NR_Meas measurementReport->criticalExtensions.choice.measurementReport = NULL; } -static void process_Event_Based_Measurement_Report(NR_ReportConfigNR_t *report, NR_MeasurementReport_t *measurementReport) +static void process_Event_Based_Measurement_Report(gNB_RRC_UE_t *ue, NR_ReportConfigNR_t *report, NR_MeasurementReport_t *measurementReport) { NR_EventTriggerConfig_t *event_triggered = report->reportType.choice.eventTriggered; @@ -1250,7 +1250,7 @@ static void process_Event_Based_Measurement_Report(NR_ReportConfigNR_t *report, break; case NR_EventTriggerConfig__eventId_PR_eventA3: { - LOG_I(NR_RRC, "HO LOG: Event A3 Report - Neighbour Becomes Better than Serving!\n"); + LOG_W(NR_RRC, "HO LOG: Event A3 Report - Neighbour Becomes Better than Serving!\n"); const NR_MeasResults_t *measResults = &measurementReport->criticalExtensions.choice.measurementReport->measResults; for (int serving_cell_idx = 0; serving_cell_idx < measResults->measResultServingMOList.list.count; serving_cell_idx++) { @@ -1289,13 +1289,13 @@ static void process_Event_Based_Measurement_Report(NR_ReportConfigNR_t *report, neighbourCellId, neighbourCellRSRP); - const f1ap_served_cell_info_t *neighbour_cell_du_context = get_cell_information_by_phycellId(neighbourCellId); - const f1ap_served_cell_info_t *serving_cell_du_context = get_cell_information_by_phycellId(servingCellId); + const f1ap_served_cell_info_t *neigh_cell = get_cell_information_by_phycellId(neighbourCellId); + const f1ap_served_cell_info_t *serving_cell = get_cell_information_by_phycellId(servingCellId); const nr_neighbour_gnb_configuration_t *neighbour = - get_neighbour_cell_information(serving_cell_du_context->nr_cellid, neighbourCellId); + get_neighbour_cell_information(serving_cell->nr_cellid, neighbourCellId); // CU does not have f1 connection with neighbour cell context. So check does serving cell has this phyCellId as a // neighbour. - if (!neighbour_cell_du_context && neighbour) { + if (!neigh_cell && neighbour) { // No F1 connection but static neighbour configuration is available const nr_a3_event_t *a3_event_configuration = get_a3_configuration(neighbour->physicalCellId); // Additional check - This part can be modified according to additional cell specific Handover Margin @@ -1304,6 +1304,15 @@ static void process_Event_Based_Measurement_Report(NR_ReportConfigNR_t *report, < (neighbourCellRSRP - servingCellRSRP))) { LOG_D(NR_RRC, "HO LOG: Trigger N2 HO for the neighbour gnb: %u cell: %lu\n", neighbour->gNB_ID, neighbour->nrcell_id); } + } else if (neigh_cell && neighbour) { + /* we know the cell and are connected to the DU! */ + gNB_RRC_INST *rrc = RC.nrrrc[0]; + nr_rrc_du_container_t *source_du = get_du_by_cell_id(rrc, serving_cell->nr_cellid); + DevAssert(source_du); + nr_rrc_du_container_t *target_du = get_du_by_cell_id(rrc, neigh_cell->nr_cellid); + nr_rrc_trigger_f1_ho(rrc, ue, source_du, target_du); + } else { + LOG_W(NR_RRC, "UE %d: received A3 event for stronger neighbor PCI %d, but no such neighbour in configuration\n", ue->rrc_ue_id, neighbourCellId); } } @@ -1365,12 +1374,10 @@ static void rrc_gNB_process_MeasurementReport(gNB_RRC_UE_t *UE, NR_MeasurementRe if (report_config->choice.reportConfigNR->reportType.present == NR_ReportConfigNR__reportType_PR_periodical) return process_Periodical_Measurement_Report(UE, measurementReport); - if (report_config->choice.reportConfigNR->reportType.present != NR_ReportConfigNR__reportType_PR_eventTriggered) { - LOG_D(NR_RRC, "Incoming Report Type: %d is not supported! \n", report_config->choice.reportConfigNR->reportType.present); - return; - } + if (report_config->choice.reportConfigNR->reportType.present == NR_ReportConfigNR__reportType_PR_eventTriggered) + return process_Event_Based_Measurement_Report(UE, report_config->choice.reportConfigNR, measurementReport); - process_Event_Based_Measurement_Report(report_config->choice.reportConfigNR, measurementReport); + LOG_E(NR_RRC, "Incoming Report Type: %d is not supported! \n", report_config->choice.reportConfigNR->reportType.present); } static int handle_rrcReestablishmentComplete(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, const NR_RRCReestablishmentComplete_t *cplt) diff --git a/openair2/RRC/NR/rrc_gNB_du.c b/openair2/RRC/NR/rrc_gNB_du.c index bc2ade3253..4e065e530c 100644 --- a/openair2/RRC/NR/rrc_gNB_du.c +++ b/openair2/RRC/NR/rrc_gNB_du.c @@ -567,6 +567,18 @@ nr_rrc_du_container_t *get_du_by_assoc_id(gNB_RRC_INST *rrc, sctp_assoc_t assoc_ return RB_FIND(rrc_du_tree, &rrc->dus, &e); } +/* \brief find DU by cell ID. Note: currently the CU is limited to one cell per + * DU, hence here, DU == cell. Modify this to look up a specific cell. */ +nr_rrc_du_container_t *get_du_by_cell_id(gNB_RRC_INST *rrc, uint64_t cell_id) +{ + nr_rrc_du_container_t *du = NULL; + RB_FOREACH(du, rrc_du_tree, &rrc->dus) { + if (cell_id == du->setup_req->cell[0].info.nr_cellid) + return du; + } + return NULL; +} + void dump_du_info(const gNB_RRC_INST *rrc, FILE *f) { fprintf(f, "%ld connected DUs \n", rrc->num_dus); diff --git a/openair2/RRC/NR/rrc_gNB_du.h b/openair2/RRC/NR/rrc_gNB_du.h index 55a0d7c2c8..bd3c4f6443 100644 --- a/openair2/RRC/NR/rrc_gNB_du.h +++ b/openair2/RRC/NR/rrc_gNB_du.h @@ -41,6 +41,7 @@ void rrc_gNB_process_f1_du_configuration_update(struct f1ap_gnb_du_configuration struct nr_rrc_du_container_t *get_du_for_ue(struct gNB_RRC_INST_s *rrc, uint32_t ue_id); struct nr_rrc_du_container_t *get_du_by_assoc_id(struct gNB_RRC_INST_s *rrc, sctp_assoc_t assoc_id); const struct f1ap_served_cell_info_t *get_cell_information_by_phycellId(int phyCellId); +struct nr_rrc_du_container_t *get_du_by_cell_id(struct gNB_RRC_INST_s *rrc, uint64_t cell_id); void dump_du_info(const struct gNB_RRC_INST_s *rrc, FILE *f); From e02aae46249f88da0acce23d7e87b2d06bd9fe45 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 8 Oct 2024 11:06:55 +0200 Subject: [PATCH 37/41] Open TODOs at RRC --- openair2/RRC/NR/rrc_gNB.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index 0f9fce815b..3739b2d5fa 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -1972,8 +1972,10 @@ static void rrc_CU_process_ue_context_release_request(MessageDef *msg_p, sctp_as f1ap_ue_context_release_req_t *req = &F1AP_UE_CONTEXT_RELEASE_REQ(msg_p); gNB_RRC_INST *rrc = RC.nrrrc[instance]; rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(rrc, req->gNB_CU_ue_id); + // TODO what happens if no AMF connected? should also handle, set an_release true if (!ue_context_p) { LOG_E(RRC, "could not find UE context for CU UE ID %u, aborting transaction\n", req->gNB_CU_ue_id); + // TODO just request the DU to release to make it happy return; } From 5c8ee816836db8c9332e5f7d971ab2f77d93e9ae Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 24 Jul 2024 16:47:37 +0200 Subject: [PATCH 38/41] Update FEATURE_SET.md with F1 handover procedures --- doc/F1AP/F1-design.md | 2 +- doc/FEATURE_SET.md | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/F1AP/F1-design.md b/doc/F1AP/F1-design.md index 4690639d5b..06f8a3115b 100644 --- a/doc/F1AP/F1-design.md +++ b/doc/F1AP/F1-design.md @@ -59,8 +59,8 @@ This is the current status: * SA * Single cell per DU * Multiple DUs connected to one CU (both CP and UP) + * Mobility between DUs connected to a single CU - Not supported: - * Mobility * NSA ## F1-C diff --git a/doc/FEATURE_SET.md b/doc/FEATURE_SET.md index eb1027bb3e..21e2f5ca8d 100644 --- a/doc/FEATURE_SET.md +++ b/doc/FEATURE_SET.md @@ -137,6 +137,7 @@ These modes of operation are supported: - evaluation of CQI report - MAC scheduling of SR reception - Support of up to 16 UEs (can be increased to 32) +- Intra-frequency handover ## gNB RLC @@ -221,6 +222,7 @@ These modes of operation are supported: - Interface with RRC - Interface with GTP-u (tunnel creation/handling for F1-U interface) - One CU(-CP) can handle multiple DUs +- Support for intra-CU mobility (across DUs) ## gNB E1AP From ad81d48285ff5164799e6352e0007d0c7854bc61 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 8 Oct 2024 11:16:46 +0200 Subject: [PATCH 39/41] MAC doc: minor clarifications --- doc/MAC/mac-usage.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/doc/MAC/mac-usage.md b/doc/MAC/mac-usage.md index e6241202c4..4c7bf4df8e 100644 --- a/doc/MAC/mac-usage.md +++ b/doc/MAC/mac-usage.md @@ -143,10 +143,13 @@ In the last lines: # Configuration of the MAC -## Split-related options +## Split-related options (running in a DU) See [nFAPI documentation](../L2NFAPI.md) or [Aerial -tutorial](../Aerial_FAPI_Split_Tutorial.md). +tutorial](../Aerial_FAPI_Split_Tutorial.md) for information about the FAPI +split. + +See [F1 documentation](../F1-design.md) for information about the F1 split. ## MAC scheduler-related configuration options From 05f10decee118b181006e5a1670ece0729b830b4 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 14 Aug 2024 17:02:33 +0200 Subject: [PATCH 40/41] Add handover tutorial Co-authored-by: rmagueta --- doc/README.md | 1 + doc/RRC/ho.mmd | 9 ++ doc/RRC/ho.png | Bin 0 -> 7905 bytes doc/handover-tutorial.md | 329 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 339 insertions(+) create mode 100644 doc/RRC/ho.mmd create mode 100644 doc/RRC/ho.png create mode 100644 doc/handover-tutorial.md diff --git a/doc/README.md b/doc/README.md index ba7e5fbd12..7fc28f061a 100644 --- a/doc/README.md +++ b/doc/README.md @@ -53,6 +53,7 @@ There is some general information in the [OpenAirInterface Gitlab Wiki](https:// - [How to run with E2 agent](../openair2/E2AP/README.md) - [How to run the physical simulators](./physical-simulators.md) - [How to setup OAI with Nvidia Aerial and Foxconn](./Aerial_FAPI_Split_Tutorial.md) +- [How to do a handover](./handover-tutorial.md) Legacy unmaintained files: - [`L2NFAPI_NOS1.md`](./L2NFAPI_NOS1.md), [`L2NFAPI_S1.md`](./L2NFAPI_S1.md): diff --git a/doc/RRC/ho.mmd b/doc/RRC/ho.mmd new file mode 100644 index 0000000000..f86e36a087 --- /dev/null +++ b/doc/RRC/ho.mmd @@ -0,0 +1,9 @@ +block-beta + columns 5 + space space DU0 space UE + CU space space space space + space space DU1 space pos["new pos"] + CU<-- "F1" -->DU0 + CU<-- "F1" -->DU1 + DU0-- "Uu" -->UE + UE-- "move" -->pos diff --git a/doc/RRC/ho.png b/doc/RRC/ho.png new file mode 100644 index 0000000000000000000000000000000000000000..dd93d6fd1aba4e2e854252a7de7e52168826f969 GIT binary patch literal 7905 zcmaKxWmuHYAMPJQK$-;sX%LZ;QbJlJC<4z5SEmZk}f5srRxXM-2zK@ zobmtWoL6UFz+QV@JI_2b-}v13AT-nziSTLgAqXORp(LjXK^WfPvo#(j`0GmvrjhQdzn|D-4dV^!Yb*=4@I)wdz&fa-Y2>2i%tPnmnH)DeHVK{P7^lS?0`o;w zGPluPQUjA#Xs2;_FVblI4Q?2mTAGOz&n4hb6$T!*sdIoFZh&JZyrla;l9ff-68!4a#>I6_-ZmI>i^nKMLcKT=?I=RRW-=O zP>`F)fG1ir2@WR{V+Lq&S?2^%%d>AMgtjfl+x2eTK@gi#4A0G#=*vn-NWc&|@0}Dl ztUW`dnf}A4*^F|nsEA>ZY#?EXQ(`qU(^4ONak%!ls;Vk9#9PvDAk}}h_x73XIOch1 zy{jDur)P5P2L?5RfpiJi<1HjTy`5Q?D7SILNU!Z@wz;)g)`t&KDJg#Y9aIM)wf3_w zRaN`@`f^4$lb_g?u|DibN-^-=moM~QEH?O~sU>V_xi+-fnVXv`XuD+Mpp>b>W7MOQ zlBVQL3PGQeJxL)Z%1B#V+Y*yz_rK%S(X^utjXt;&;$oM51zPjGMs-tjbLS^Je^Z2f zD*SE>_|3Mp-yfH?bamk(yKu3wy|4DW1_lO_1ueTjC%C$*9X!Owe4d8GH2fTb{OjvU z#FO}1q@Ft8to7U0+9^N#$ZB9RzmR6fPblVfvT=3(0S+gFAe^@BhK94&kUl}%8^z;L z5)wYbzdJkq{r!z@=(%BL&q4EEw7kZn5Ha0Z)3CR(1qN1V>cA_s^79FWWU$B0Tq~f)-vZ}Ag3GUZl!k&|e)4|X1Ysyl8 zp`y~-8iLsJ5QWjV({65(ac!e8efB7v@-I*JdeXXXVAuJjrSr41L{M2xjqm=_9qy-s zf(feXO01vtuQ=UX2L^U0E3NjD)beVptGjoXS5_#<$e3Y>ABOfiI`=|k$nZn6wPfTb_AVWbg9I4C7 zrd1xggZhCCGdqyHC&v=|^V-XDCgwe2i-L7$I!)}8VH7N>d-|;5tdjb zwm7Nt74iq0=ATPo_V(6FevK(KG+Q8=rCpiFaNZ~>9EA+7@66VNoHjjJk=9>YUItm9 zDtUWrB>}RyqJl-LnFE4oQ_8=6TO(nd3rYlU9E9uU<9#J)ej~QGzh6*Lz`@I#n6T&G zkRt4Ganbbg<;d7rg5PLzGR;Ft`hh%WXJ@csUwBP?cc!XAkZ}E7la%xY(X+bRQ2}E> zdL6FaJCE+jglIMiY1J|$C48^WJ}h@0pK}#c;1e7!njF{FttawYxwyK%pR5Rz$^2FH z;X|)pafDGdr|UqflB}$!i%ZA(;UgoX`=fcy7a@o!!-HA3cb%Qzh>3ZgJ)2Zjjdog- zSIqNrSmou~C#R-rtDYJ)Byq$lQSusdm=rfO z=+|JgnO(WN(bE@x-0>h+at_5Mf2775*WK9I2t%}wjg5_t#?!>!;Itx`dZ5$Dp8qz?+))lTr7dXs=+Xu*Hpz5T}WsUsu~wO-;QyI(}zt z9A|2lh(f*ot=Ib-1kX-Okk$4!oeq5LZ^MT%BL1Ac(^IO*4#&q(8=sxsPI@{6OYFw_ z`ug&6+grZmh-l~N{1{_d>#Y(tjjejcuLlb7prQzD8V(>F_{7Q9m?3wQIE zQzZL}+jS|!3QI^Lc8v%Y%s3!H-$7QJ-u;H;11r-=Cp!2}{&d1`4w zcFEp%qZNX1CnqNrW9ayWgnZ|{TwPr&CbzS)Sct<$CnsO9#i3NbB_`^bo0}^s;l)$y zlM_3FHto7SK}JgIc-m06j6;Uk@oz?5U0rfEm5tL8)Dx>3``P4_6a$UDUFn_V? z8RD?EMMv((k6*CDf?AZ6l->%U4tSoOpP$do%~fbUe*Bmwy2fE%Qn5=AmF;-DdW(kSe_I6g`+FOcGhCH=ZRY#knd33~K{g#&(7bDhB z9URc|k(AN*pM0BVBNgdu;Zspo?j0L*0sBx@dX)D6^lf3lGP$Xu!bC#Q67nu-Ur0U%X#{V{v!?@ec^G`5w`R!k^Ogx3&gKGnMIo zZ+VZxf*7QH#jT6TvdIKc;?AZd|9+~Wq2cJrM?nRG6^7{R@Be97Qd3hC5P(6r^8kjZ ztE)?{q2Qki@Vk9#*TMB^qd{0zRn=RPv5gNfTxluEgG{C%y5CHsoS4zhp^P0J9gfNd ztS}uN9iQVdJqY4VxH#!&!!`|Pid72D->4D_ub&RBt%TZt=||f0v!Bky#1IRm^e;Nv z*xEWdIdw(TcA1ucQ%mbH7XVcN6a`0-nUsSmXBQU0Kv|9}%KWF+XG;QF8 zfYi`>^X83)#;-+3eeN%@v1E}QG*NL3nEVaYq7kFQMw@Aj2317AAH4M(J6lqZ=pYbr z@dR`e)UtPQ@X^J^qFn#we)6mD1PMtk1pGFe0IR;Yjm^+-W(@sb zY6gafmp5vfno&Q0u2Pxg^a%fS`TNFZtFy0KSdZI ziqFEYfWpE;0JN>i^={}U@D=E9erJneU%!3@w7j;m;_l`3cWY~Hb+y|*DKW9@EuV_2 zDlH9-CfM<)C}KL1GK=0XV2Wac+QOB4@xEOrC%TY73Y9y8j*@0#3J3@=>x>#68%q=S zIB34TMo&!WV_{)|SS2Ar0=9vqy}T~d#N4v7vb=qK9&>YpR|R6M)bJ;0NY~fb7Fi(U z|CI=f9t!AJ0+-%z^N#=-qoboi@dj&v!{IJ2)g#e*O&h-kKR0qv!vdrU84RZNY^jFO zeVv^+>6!)F3`pF{YcuM#wKbgHefNf-AgolKJJ{ITy1F%0RS>kW4wpJxz=EXZ@gv0>wG6yS1&C9-Au7Z*L1o1+zK`!(uCOOBt^^n z?&or9j&5{pBMCoSU*>9ZCUC|o0bT}WGd+DS2$!U_wRLyC84a2%VDJ=><6*(UzwIB! zDv64TCD=C{$cJ?wv5?|5U%M6|H-H@I>tk$fr5v&j4Glf|N47+?|9G>aNwVkGUz(7T zGXcL@3{!pIT^!jpIx-Sy9C&kcL&6}Q^vr3kEsO+27mF=`(72=|c0WcRpVh8M?0%lP zMMYH9A`iqao#o{Vygd{;=PIp`0N^2iB+^7x)xyK0e47*5)#z~;(dR`9_7Mjc7gwCY zL{k$}Jy-J`*d4HQ;zW3OZ!0JLuJ*&Enf#r&ApgorCt2B`goFfeDACbGQ78?07<~(T!JKv zqEW@t z*?6I^qonj(UFteMJzdgvoSTl=?fhVMy4HbF(i_Mru0KKv;V}UiJJSwN1~Q~v&~yDo zdV{anr+9dxs;XoOLvg=L|9`kGzm);ol%m3BtPsRJh}Mrk4YjmJgM+buDBppYU;l7;fj3bbui9?|PJd1uOvx8qHz{e-e*eOE@>o_R`t%;v7 zg%2Kx&+X};u#NUl`!|gA^{J0^{V|;Z9xxyuQATE7@S#*tT*|=8flS#joJ#?`_62`?G1*`;F42RHt*r}yNayD*qlJZ>LMhj0`^#K< zCE{KugoPvk4EU(gf-sb*%U0F9(G_Lur!?Zf2L>cRu7sEGzpadACXHA`qj_P73zc^4Jrrf%mg@g2KYJU%yHV@&-sZL<5nWA?8LLO4&$^kb6^Q9Xl26DQ_~oIAh1< zm9Mn6o*AUi?y}Eded0nBO-TyIUJrR)RaJbyrIg2kIUq71Hn=H!r>CboIxMcv_R$uy zo}P6G#0z`--OCiDG<(3=*_qS&0GxUpJ$EM*PC&^$6m0ahbmMFw$4gzSiTBe5bL>4r zG_|n(%#RPtK$iX~v9q^da}-%!UDYc!2B8wtE1jO6UQ&V{z&AGyYzQVz4pztp$U-mS zd32nh2tzz|+cng|)*<@T98N5ct3G^R=BKt`WI@3u*wBT-Xl>4r`E{?ey+zO)z*EBE z6TOMNVxpoeQp0(dHDxt=YQC|C`1_|gNTUG>`}DG z#e&g4lm%Lx71k${m6wMbS6fqC8`-h6GhLg4LJ@Uxd;ortySVT1)>v$8EN2-#KPDnF zvbU!vFhC53U}a&E8Kq<%euZIVWW;yh&cGl=pSz^AG|U4__q+Z+cp7MspjJ!(PfCeZ zfST|JhtRK8E6ji)pfl6cjkUB6C#^D`%LggtLUF{IUnN6Y7C*>twmEJ=ahL@?7cqs| zROF3&8|4qc66PAtpJHVjBMQ?6e>Jrc73_^J$`}|JfC3eGhk%mD@a$r&p`n4d`Km%4 zZfa`k?cLC~{Ijw$u5FRx-o5h4_Xs(VfLM3JJ4_opIEI0YQyb_uuP83wS?f;$sk@k4 zzitr0=)8Q_!A4a4(@EgI?}gS{3{O~)7uB1HgStIzojX_r?MA4dx6xQX@|&+CvvLc6 zCR9;HosMM)pl5x6Xdt8LeRuH1e-P*}pnEJU&%J#;Jf6jMoAZHeWAI6ZS!7}9VPilk zK!H7TVxypsknA3|b+<|?@f>-$O^K8lX5Rdq+7RY7??O;7hebyJw2=n(1E6_bUF%bO z5ZI2S&LCp|Q-Hnn^YarEs|P&WyS4221NcJFug+-NqM{{@O40ldwV z&`_!hd~Zo9k%{$@-0zY^ZaY&I=G}&q@6^=?n|#CqgM4;pFXb_wkY?jP9l6h-!A=n% z4OJ^BD-#S90xT~rEv>H?3u-xA4)xC}Jxf-z*?PXAW@KRCbbZlbP+MX*leD(8Blc^p zzyEfw?pUi_Kc@^Md>9Lswiuwn-oCs~9s1h90Az~*brgx@*+MCUL}1Iz{Ot9Zl1(f2 z?a^2%UQX9%E%yzlPF?#3{_z( zeO(D3&U!6%>uoXDkQDF~CFxUxK-^Ef)Eujc#wje~`ofF6IGK)$dqk2DPoY`hxw!c1 zaINv9>|+kNmHBpYK09;l@1`aU1j6?Cn5gO85-VfydaG!bkEpl7BLFgSU*gNNWL8((sq}UE`f5?QKd<`sBvr#8KOKb%X>4?QH7xB^oSc+Y zFp>2N8z3L#eL8!qp!g_N%I^`UP50?M+T-Lq{>@Zg4!5v?fTSanQ|}iE9UTjUT3>u< zZ!bwhvo$oPX>9BsMTD!1%QM1*_jY#nHa16lNfYZF*qHoJ4sn5tXEk_J@ow}Mco_qz zCyEfn63hP3!UHh!$<8GS-S4VOr4Nqs_{@*fKgg_o0QJgi3P(VC+;z_v;G_ z>uQeVg@l}g#igf{n>1~Qj%XNO#djPC304CYjsZD}+_|{GAtoO1*+sT3rl!u^+?)a~ zSy|+VZ~eFFhQ1+$+>bVDJ3BAbCC^*O65wV}TstBL^R;LH{;AANJ-@*D@gs`yNvHi{ zE3)}~P0Ywh;*K#9f;h|1)3cowb|Ca0wEzlCND(2FTBoZW|KkUrIRmY|JvU3NzHTS~ zlS)3}T5cX5Y_JgZnHl|(lBlJnJdL24S)aY@JEo#y$T<4xsi{AIZh^<3ql2ociAhV# z-rfYx;$XTCCUkZ6jZVsUZ(|ti;luX@+C8QouL>dv8)G+^F7h%nW7LEbqE8;eUS#02 z#Nw@5>d>>oUf)otr2(Q`?mCr~xmswRth{CWA51S07FER)>2&t@&w{&%{0$E1Ykodd z^1C4hJoIWgN3%emdu{)Rr>EbGjPFH7UF(Ms)ZLw8Y6|ncl{k%4r&LrN{PA%$)wDE} z@-x}BhLO=L6B8=>G|26*i*tT|zmF9CeAGb*; z)GNJHLw1U($!EZ&YzrF&+Hx>Mues?fmdAO8fh#xZ-d$`=gj|-U%W5xNP$UbO``}h7 zRoDR-sfVk*R;N4D@OST?I5y+o2e(xl1Q~xV%(; zW?>;8VQ0)l(K+ei4XpZvF=}8i3kjJO7u#X{x=$@VjMC6M73W3Aq|io6Gl9;goI8@5 zs?5e_sjGW!6$LcdXL>0w-FIeBD8t&GiPhi5_Wof{Pacg7eKIyO z`fzjgLQk&~gf%=QA_5lRPclmd?yZ0yeHWW0XfboxjV2&CxbJc^k8yOg86{|oK^#`^ zeZIXj{fC6H$=cdlE{}G4A_|xoiW3|nboMhMYpcDl9g+<&pqs11Nu*Tz-f6}gV`Jyj z-9Z8>HcW_3?dn%gkLBrZF_*5l0!!>bnivmdq-9@H5PTwCVp=3M5EMvrfmP7%i;MdK z#^E>Ifze|_(07w&!W;G6QB`o0_JD=*wE;5LsJe!s(ha>&{)iX|&`({>Aec zJm=g2cKzjA3YweyI|P~drTK181i1W#d3rt6)ioP0y^ZKT(q4G10V^fLe7Pj(J*)&P z-P>#KYWJpNV95PWODv9{dNcZ*iPQ$%b^h-LR24UXI@;Q?gDBdW6fgMA^Q@qMx1C@M zFf(`B*)#Df4riXCD6FV~aNd;Z+Czzc;|(YU%$j=oa_* X{DNt{t`iCFtf3e3YH}qqZv+1iT(??j literal 0 HcmV?d00001 diff --git a/doc/handover-tutorial.md b/doc/handover-tutorial.md new file mode 100644 index 0000000000..8595fe05d4 --- /dev/null +++ b/doc/handover-tutorial.md @@ -0,0 +1,329 @@ +This tutorial explains how to perform handovers. For the moment, only F1 +handovers are supported. + +[[_TOC_]] + +# Considered setup for F1 handover + +We consider one CU and two DUs, connected over F1. The UE is initially +connected over the radio interface ("Uu") to DU0. Via movement to a new +position ("new pos"), it will trigger an event such that the CU triggers a +handover of the UE from DU0 to DU1. Alternatively, a manual trigger can do the +same. + +![F1 Handover setup](./RRC/ho.png) + +# Steps to run F1 handover with OAI UE + +Measurement reporting and processing of RRC Reconfiguration for Mobility are +not completed at the UE. Nevertheless, it is possible to make simple handover +tests without any radio setup, on a single PC, with the OAI UE, in RFsimulator. + +## Build with telnet support + +Since the UE does not support any measurement reporting, it cannot trigger a +handover on its own; it has to be triggered manually through telnet. Thus, +build both gNB and UE as well as activate the build of telnet to that purpose: + + ./build_oai --ninja --nrUE --gNB --build-lib telnetsrv + +## Run the setup + +This tutorial assumes you have a core network running; [refer to the +corresponding tutorial](./NR_SA_Tutorial_OAI_CN5G.md) if this is not the case +yet. + +We will use the TDD configuration files in the repository for the +[CU](../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-cu.sa.f1.conf) as well for +[DU0](../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band78.106prb.rfsim.pci0.conf) +and +[DU1](../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band78.106prb.rfsim.pci1.conf). +Note how the DUs differ in their DU ID (for identification at the CU), nr +cellid (global identification), physical cell ID (identification through UE), +frequency (limitation at OAI UE), and IP address. + +Make sure that using RFsimulator and the CU and each DU, you can achieve a full +connection of the UE (independently, i.e., running one DU a time). Once this is +done, follow below steps to trigger a handover: + +Start the CU including telnet support: + + sudo ./nr-softmodem --sa -O ../../../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-cu.sa.f1.conf --telnetsrv --telnetsrv.shrmod ci + +Start DU0: + + sudo ./nr-softmodem --sa --rfsim -O ../../../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band78.106prb.rfsim.pci0.conf --rfsimulator.serveraddr 127.0.0.1 + +Start the UE, and let it connect completely: + + sudo ./nr-uesoftmodem --sa -C 3450720000 -r 106 --numerology 1 --ssb 516 -O --rfsim --rfsimulator.serveraddr server + +Note how the RFsimulator roles have been switched, and RFsim server is at the +UE side; _this is important_. Replace `` with the UE configuration +matching your core. If you followed the CN and oaiUE tutorials, you can remove +`-O ` and replace it with `--uicc0.imsi 001010000000001`. + +Once the UE is connected, start DU1: + + sudo ./nr-softmodem --sa --rfsim -O ../../../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band78.106prb.rfsim.pci1.conf --rfsimulator.serveraddr 127.0.0.1 + +Once DU1 is online, you can trigger a handover by issuing this command + + echo ci trigger_f1_ho | nc 127.0.0.1 9090 && echo + +You should see how the UE switches from one DU to another. See additional +information further below. + +A number of remarks: + +1. It is important that you start DU0, UE, DU1 in order, and having UE connect + to DU0 before starting DU1. This is because we don't employ any channel + emulation, and the UE could not decode the SIB1 of DU0 to connect. +1. The RFsimulator roles are switched. Typically, the gNB RFsim acts as the + server and the UE as a client. However, RFsim is limited to one server with + multiple clients. Since the UE should be able to connect to both DUs, it has + to act as the server, and both DUs are a client. +1. If you see errors `could not open a socket` and/or `Could not start the RF + device`, this means that RFsim could not be started. Handover will not work; + please refer to the preceding point to fix this (i.e., run the UE as the + RFsim server). +1. In some cases, if the RFsim server is at the UE, the whole system can block; + in this case, stop UE and all DUs and restart (the CU can keep running). + +## Additional information to the manual HO trigger + +You can trigger the handover manually by logging in through telnet: + + telnet 127.0.0.1 9090 + +and then manually typing `ci trigger_f1_ho`. + +The command using `nc` (`netcat`) above triggers handovers directly from bash. +It does the same as logging in through telnet otherwise, and is a shorthand. + +The full command is + + ci trigger_f1_ho [cu-ue-id] + +`cu-ue-id` is optional. If only one UE context is present in the RRC, it will +trigger the handover for this UE in a round-robin fashion across all DUs. If +there is only one DU, the handover request will be rejected. Similarly, if +there are multiple UE contexts present at RRC, the handover will be rejected, +and you have to manually type the CU UE ID. You can see a list of all UEs in +the file `nrRRC_stats.log` that is printed periodically in the working +directory of the CU. + +# Steps to run F1 handover with COTS UE + +You can do handover across DUs with a COTS UE. Note that these DUs should be +separated by at least multiple meters to ensure that the UE will receive +different signal strengths when moving between cells. + +We only support intra-frequency handovers yet. We have verified with USRPs +only, although other radios should work as well. + +For UEs, we verified Quectel modules and iPhones. Note, though, that not all +phones might work; for instance, we did not achieve handovers with a OnePlus +Nord, yet. + +## Steps + +First, make sure that you can run both DUs with the CU independently. Use the +same radio hardware for both radios to ensure that both cells can be received +equally good by the UE. + +In order to enable handovers (triggered by the UE), you have to configure the +neighbour relation of the DUs at the CU. To do so, proceed as follows: + +1. To simplify filling the right values in the neighbour configuration, you can + rely on the information the CU has about both DUs. Start the CU and both + DUs. Navigate to the directory from which you started the CU, and print RRC + statistics: + ``` + cat nrRRC_stats.log + ``` +1. Fill in the `neighbour-config.conf` configuration file as shown below, and + `@include` it in the CU file. +1. Start the CU and both DUs. +1. Bring the phone close to one cell, and leave flight mode. It should connect + to the DU to which it is closer. +1. Move the UE towards the other DU; it should trigger an "A3 event" (Neighbour + Becomes Better than Serving), and the CU will trigger the handover to the + other DU. + +The output on the terminal should be the same as with RFsim. If no handover is +triggered: + +- Make sure that both DUs use the same hardware. +- Make sure that the UE sees both cells. For instance, you can switch to flight + mode, go closer to the other DU, and switch off flight mode -- the UE should + connect to that second UE. +- We did not manage handover with every phone yet -- make sure you use one of + the list provided above. + +You can also force a handover through telnet as described above. (In fact, the +decision about a handover is always at the network-side, the UE only "assists" +through measurements telling the CU that one DU is stronger than others. +Hence, "forcing" a handover just means that you manually trigger the handover, +instead of waiting for UE measurement report.) + +## Example neighbour configuration + +Below is an example neighbour configuration. It is based on this DU information +gathered from `nrRRC_stats.log` at the CU: + +``` +[1] DU ID 3585 (gNB-in-docker) assoc_id 4161: nrCellID 11111111, PCI 1, SSB ARFCN 643296 + TDD: band 78 ARFCN 642024 SCS 30 (kHz) PRB 106 +[2] DU ID 3584 (gNB-in-docker) assoc_id 4163: nrCellID 12345678, PCI 0, SSB ARFCN 643296 + TDD: band 78 ARFCN 642024 SCS 30 (kHz) PRB 106 +``` + +Note how both DUs have one cell on the same frequency and the same radio +configuration. From this, fill the neighbour list as shown below. + +Concretely, the first cell is `12345678` (on DU `[2]`), and it has `11111111` +(on DU `[1]`) as its neighbour; hence in the first block, you fill +`physical_cellId` and other values for DU `[1]`, and vice versa. + +The below configuration further enables periodic measurements, A2 event +("Serving becomes worse than threshold"), and A3 events ("Neighbour Becomes +Better than Serving"). The A2 event can be disabled by setting `enable = 0`. A3 +events cannot be disabled as of now. Further, the A3 events can be made +specific to cells; `cell_id = -1` means "any cell". + +``` +neighbour_list = ( + { + nr_cellid = 12345678; + neighbour_cell_configuration = ( + { + gNB_ID = 0xe01; + nr_cellid = 11111111; + physical_cellId = 1; + absoluteFrequencySSB = 643296; + subcarrierSpacing = 1; #30 KHz + plmn = { mcc = 001; mnc = 01; mnc_length = 2}; + tracking_area_code = 1; + } + ) + }, + { + nr_cellid = 11111111; + neighbour_cell_configuration = ( + { + gNB_ID = 0xe00; + nr_cellid = 12345678; + physical_cellId = 0; + absoluteFrequencySSB = 643296; + subcarrierSpacing = 1; #30 KHz + plmn = { mcc = 001; mnc = 01; mnc_length = 2}; + tracking_area_code = 1; + } + ) + } + ); + + +nr_measurement_configuration = { + Periodical = { + enable = 1; + includeBeamMeasurements = 1; + maxNrofRS_IndexesToReport = 4; + }; + + A2 = { + enable = 1; + threshold = 60; + timeToTrigger = 1; + }; + + A3 = ({ + cell_id = -1; #Default + offset = 10; + hysteresis = 0; + timeToTrigger = 1 + }) +}; +``` + +# Handovers triggers and NTN + +Typically, in terrestrial networks, channel measurements as well as criteria +such as load in base stations, is used to determine when and where to handover +a UE. + +## NTN + +Doppler spreading and time selectivity of the channel are already a challenge +for conventional terrestrial networks. However, in the context of +non-terrestrial networks (NTN), and low-earth orbit (LEO) systems, the +satellites can have speeds up 7.56 km/s, which is much faster than 0.14 km/s of +a high-speed train in terrestrial networks. Also, the delays in LEO are more varied +and longer, and the path loss is larger, because the communication distances is up +to 10 times longer than in terrestrial networks. Moreover, downlink in LEO +presents a high interference from adjacent satellite beams, and all these +features contribute to reduced received signal strength variation in these +networks compared to terrestrial networks. Typically, the criteria used in +algorithms presented in conventional terrestrial networks to trigger a handover +from a gNB to another one is based on signal strength measurements (cf., A3 +event above). However, the reduced received signal strength variation in NTNs +make these algorithms inefficient for LEO systems. Therefore, algorithms with +criteria for handover triggering that address the specifics of LEO systems are +crucial for an efficient handover processing that ensures a robust +communication with low dropping probability. Some criteria for handovers in NTN +are as follows: + +- **Measurement-based triggering**: This method is based on signal strength + measurement, and as stated above, it may not be efficient. The triggering + thresholds and which measurement events to use as triggers, as reference + signal received power (RSRP), reference signal received quality (RSRQ), or + received signal strength indicator (RSSI), should be configured. This method + relies on UE estimates and established channel estimation techniques, however + it would require neighbouring cell lists which can be hard because the + fast-moving of satellites leads to a fast cell coverage deviation. + +- **Location-based triggering**: This method is based on UE and satellite + location, which can be applied jointly (or not) with another trigger as the + measurement-based trigger. For instance, for a deterministic satellite + movement, it is possible to predict the configure triggering condition, and + the initial association of the UE can be performed based on the distance with + the nearest satellite, because we can know the location of the UE and NTN + satellite to compute the distance. + +- **Elevation angles of source and target cells based triggering**: This method + is similar to the previous one, but it is based on the largest elevation + angle. + +- **Time/timer-based triggering**: This method uses triggering conditions based + on UTC time or a timer-based solution, which can also be applied jointly (or + not) with another trigger as the measurement-based trigger. The timer-based + handover trigger considers the deterministic satellite movement to predict + the time duration for which the satellite’s footprint covers a certain zone. + +- **Timing advance value-based triggering**: This method uses the timing + advance value (independently or jointly with another trigger) to trigger a + handover to the target cell. It is appropriate to overcome the Random-Access + preamble reception issue, where the UE needs to pre-compensate the instant + which sends the preamble. However, UEs with GNSS support are required to + perform this method. + +## Simple location/time-based trigger + +A location-based handover trigger, somewhat aligned with 3GPP Rel.17, taking +advantage of deterministic satellite movement, can be implemented whereby it is +assumed that the locations of the UE and the NTN satellite are known. From a +practical point of view, this means that we know how long it takes to perform +the handover, and therefore we only need to implement a timer. + +It is possible to modify the source code to trigger a time-based handover, or +combine with other methods. If all what is required is a trigger every 15 +seconds, you can also resort to the telnet-based handover trigger above, and +run in a terminal: + +```bash +while true; do + echo ci trigger_f1_ho | nc -N 127.0.0.1 9090 && echo + sleep 15 +done +``` From f2c6fce30ffde7685a4d0f1e2209edaf755454f5 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 7 Oct 2024 19:22:34 +0200 Subject: [PATCH 41/41] Add 5G RRC documentation --- doc/RRC/rrc-dev.md | 318 +++++++++++++++++++++++++++++++++++++++++++ doc/RRC/rrc-usage.md | 99 ++++++++++++++ 2 files changed, 417 insertions(+) create mode 100644 doc/RRC/rrc-dev.md create mode 100644 doc/RRC/rrc-usage.md diff --git a/doc/RRC/rrc-dev.md b/doc/RRC/rrc-dev.md new file mode 100644 index 0000000000..d511421b01 --- /dev/null +++ b/doc/RRC/rrc-dev.md @@ -0,0 +1,318 @@ +This document contains documentation for the 5G RRC layer, destined towards +developers. It explains the basic working of the RRC, and various UE procedure +schemes (connection, reestablishment, handover) including their interworking +with other layers. + +User documentation, such as general configuration options, are described in [a +separate page](./rrc-usage.md). + +[[_TOC_]] + +# General + +5G RRC is basically an ITTI message queue with associated handlers. It +sequentially reads received ITTI messages and handles them through the function +`rrc_gnb_task()` (constituing the thread entry point function). In this +function, one can see three main groups of messages in a big switch statement: + +- NGAP (messages starting with `NGAP_`) +- F1AP (messages starting with `F1AP_`) +- E1AP (messages starting with `E1AP_`) + +For each message, a corresponding handler is called. The messages are roughly +named according to the 3GPP specification messages, so it should already be +possible to find the message in the switch based on a message name in the spec. + +Note that RRC is inherently single-threaded, and processes messages in a FIFO +order. + +# Sequence Diagrams of UE procedures + +The following section presents a number of common UE procedures for connection +establishment&control, bearer establishment, etc. The intention is to help +developers find specific functions within RRC in the context of these +procedures. For more information on message handlers at F1 and E1 layers, +please refer to the respective [F1 documentation](../F1AP/F1-design.md) and [E1 +documentation](../E1AP/E1-design.md). + +For more information on these procedures, please also refer to 3GPP TS 38.401 +(NG-RAN Architecture Description) and O-RAN.WG5.C.1-v11 (NR C-plane profile). + +A lot of the following diagrams would show an exchange between CU-CP, DU, and +UE, in which the CU-CP forwards an "RRC Message" to the UE via an F1AP DL RRC +Message Transfer through the DU, and correspondingly receives the "RRC Message +Answer", as follows: + +```mermaid +sequenceDiagram + participant ue as UE + participant du as DU + participant cucp as CU-CP + cucp->>du: F1AP DL RRC Msg Transfer (RRC Message) + du->>ue: RRC Message + ue->>du: RRC Message Answer + du->>cucp: F1AP UL RRC Msg Transfer (RRC Message Answer) +``` + +To better utilize horizontal space, these exchanges have been collapsed as +follows, but should be read as the above exchange: + +```mermaid +sequenceDiagram + participant ue as UE + participant du as DU + participant cucp as CU-CP + cucp->>ue: F1AP DL RRC Msg Transfer (RRC Message) + ue->>cucp: F1AP UL RRC Msg Transfer (RRC Message Answer) +``` + +## Initial connection setup/Registration + +This sequence diagram shows the principal steps for an initial UE connection. +This can either happen through a _Registration Request_ (e.g., UE connects +"from flight mode"), or a _Service Request_ (i.e., UE connects after leaving +coverage). Both requests are handled similarly by the gNB, and basically +distinguish themselves, from the point of view of the gNB, by having PDU +sessions in the NGAP Initial Context Setup Request present (Service Request) or +not (Registration Request, with following PDU Session Resource Setup Request +procedure). + +```mermaid +sequenceDiagram + participant ue as UE + participant du as DU + participant cucp as CU-CP + participant cuup as CU-UP + participant amf as AMF + + ue->>du: Msg1/Preamble + du->>ue: Msg2/RAR + ue->>cucp: F1AP Initial UL RRC Msg Tr (RRC Setup Req, in Msg3) + Note over cucp: rrc_handle_RRCSetupRequest() + cucp->>ue: F1AP DL RRC Msg Transfer (RRC Setup, in Msg4) + ue->>cucp: F1AP UL RRC Msg Transfer (RRC Setup Complete) + Note over cucp: rrc_handle_RRCSetupComplete() +%% TODO: when is RRC connected? + cucp->>amf: NGAP Initial UE Message (NAS Registration/Service Req) + Note over amf,ue: NAS Authentication Procedure (see 24.501) + Note over amf,ue: NAS Security Procedure (see 24.501) + amf->>cucp: NGAP Initial Context Setup Req + Note over cucp: rrc_gNB_process_NGAP_INITIAL_CONTEXT_SETUP_REQ() + Note over cucp: rrc_gNB_generate_SecurityModeCommand() + cucp->>ue: F1AP DL RRC Msg Transfer (RRC Security Mode Command) + ue->>cucp: F1AP UL RRC Msg Transfer (RRC Security Mode Complete) + Note over cucp: rrc_gNB_decode_dcch() (inline) + opt No UE Capabilities present + Note over cucp: rrc_gNB_generate_UECapabilityEnquiry() + cucp->>ue: F1AP DL RRC Msg Transfer (RRC UE Capability Enquiry) + ue->>cucp: F1AP UL RRC Msg Transfer (RRC UE Capability Information) + Note over cucp: handle_ueCapabilityInformation() + end + opt PDU Sessions in NGAP Initial Context Setup Req present + Note over cucp: trigger_bearer_setup() + cucp->>cuup: E1AP Bearer Context Setup Req + cuup->>cucp: E1AP Bearer Context Setup Resp + Note over cucp: rrc_gNB_process_e1_bearer_context_setup_resp() + cucp->>du: F1AP UE Context Setup Req + du->>cucp: F1AP UE Context Setup Resp + Note over cucp: rrc_CU_process_ue_context_setup_response() + Note over cucp: e1_send_bearer_updates() + cucp->>cuup: E1AP Bearer Context Modification Req + cucp->>ue: F1AP DL RRC Msg Transfer (RRC Reconfiguration) + cuup->>cucp: E1AP Bearer Context Modification Resp + ue->>cucp: F1AP UL RRC Msg Transfer (RRC Reconfiguration Complete) + Note over cucp: handle_rrcReconfigurationComplete() + end + Note over cucp: rrc_gNB_send_NGAP_INITIAL_CONTEXT_SETUP_RESP() + cucp->>amf: NGAP Initial Context Setup Resp (NAS Registration/Service Complete) +``` + +Note that if no PDU session is present in the NGAP Initial UE Context Setup +Req, no F1AP UE Context Setup will be observed during this initial phase. + +If there is no PDU session set up during NGAP Initial Context Setup Req, the UE +typically requests a PDU session(s) through a NAS procedure, which is basically +the same code paths as the above optional PDU Session setup during an NGAP +Initial Context Setup procedure: + +```mermaid +sequenceDiagram + participant ue as UE + participant du as DU + participant cucp as CU-CP + participant cuup as CU-UP + participant amf as AMF + + ue->>cucp: F1AP UL RRC Msg Transfer (RRC UL Information Transfer) + cucp->>amf: NGAP UL NAS Transport (NAS PDU Session Establishment Req) + amf->>cucp: NGAP PDU Session Resource Setup Req (NAS PDU Session Establishment Accept) + Note over cucp: rrc_gNB_process_NGAP_PDUSESSION_SETUP_REQ() + Note over cucp: trigger_bearer_setup() + cucp->>cuup: E1AP Bearer Context Setup Req + cuup->>cucp: E1AP Bearer Context Setup Resp + Note over cucp: rrc_gNB_process_e1_bearer_context_setup_resp() + cucp->>du: F1AP UE Context Setup Req + du->>cucp: F1AP UE Context Setup Resp + Note over cucp: rrc_CU_process_ue_context_setup_response() + Note over cucp: e1_send_bearer_updates() + cucp->>cuup: E1AP Bearer Context Modification Req + cucp->>ue: F1AP DL RRC Msg Transfer (RRC Reconfiguration + NAS PDU Session Establishment Accept) + cuup->>cucp: E1AP Bearer Context Modification Resp + ue->>cucp: F1AP UL RRC Msg Transfer (RRC Reconfiguration Complete) + Note over cucp: handle_rrcReconfigurationComplete() + Note over cucp: rrc_gNB_send_NGAP_PDUSESSION_SETUP_RESP() + cucp->>amf: NGAP PDU Session Resource Setup Resp +``` + +## Reestablishment + +The following sequence diagram shows the principal steps during a +reestablishment request. When handling the RRC Reestablishment Request at the +CU-CP, it notably checks if this UE is already known, if the UE is still at the +same DU as it used to be previously (might change because of, or despite +handover), and other validation checks. If all checks pass, the CU-CP continues +with the reestablishment procedure; if any check fails, the CU-CP tries to +release the old UE at the AMF, and continues with the "normal" connection setup +(registration/service request) described above. + +```mermaid +sequenceDiagram + participant ue as UE + participant du as DU + participant cucp as CU-CP + participant cuup as CU-UP + participant amf as AMF + + ue->>du: Msg1/Preamble + du->>ue: Msg2/RAR + ue->>du: Msg3 w/ RRC Reestabishment Request (old C-RNTI/PCI) + Note over du: DU creates UE context for new RNTI + du->>cucp: F1AP Initial UL RRC Msg Tr (RRC Reestab Req, in Msg3) + Note over cucp: rrc_handle_RRCReestablishmentRequest() + alt UE known and at the original DU + Note over cucp: rrc_gNB_generate_RRCReestablishment()
SRB1 PDCP reestablishment + cucp->>du: F1AP DL RRC Msg Tr (old gNB-DU-UE ID) + Note over du: Reuse configuration of and delete old UE
(in dl_rrc_message_transfer()) + du->>ue: RRC Reestablishment + ue->>du: RRC Reestablishment Complete + du->>cucp: F1AP UL RRC Msg Tr (RRC Reestab Complete) + Note over cucp: handle_rrcReestablishmentComplete() + Note over cucp: cuup_notify_reestablishment() + cucp->>cuup: E1AP Bearer Context Modification Req + cucp->>ue: F1AP DL RRC Msg Transfer (RRC Reconfiguration) + cuup->>cucp: E1AP Bearer Context Modification Resp + ue->>cucp: F1AP UL RRC Msg Transfer (RRC Reconfiguration Complete) + Note over cucp: handle_rrcReestablishmentComplete() + else Fallback* + cucp->>amf: NGAP UE Context Release Request + Note over cucp: rrc_gNB_generate_RRCSetup() + Note over ue,amf: Continue with connection setup (registration/service request) + end +``` + +## Handover + +The basic handover (HO) structure is as follows. In order to support various +handover "message passing implementation" (F1AP, NGAP, XnAP), RRC employs +callbacks to signal HO Accept (`ho_req_ack()`), HO Success (`ho_success()`), +and HO Cancel (`ho_cancel()`). These can be used to trigger the corresponding +functionality based on mentioned "message passing implementation". + +The following sequence diagram shows the basic functional execution of a +successful handover in the case of an F1 handover. Note the callbacks as +mentioned above: + +```mermaid +sequenceDiagram + participant ue as UE + participant sdu as source DU + participant tdu as target DU + participant cucp as CU-CP + participant cuup as CU-UP + + Note over ue,sdu: UE active on source DU + alt HO triggered through A3 event + ue->>sdu: RRC Measurement Report + sdu->>cucp: F1AP UL RRC Msg Transfer (RRC Measurement Report) + Note over cucp: Handover decision (A3 event trigger) + else Manual Trigger + Note over cucp: Handover decision (e.g., telnet) + end + Note over cucp: nr_rrc_trigger_f1_ho() ("on source CU") + Note over cucp: nr_initiate_handover() ("on target CU") + cucp->>tdu: F1AP UE Context Setup Req + Note over tdu: Create UE context + tdu->>cucp: F1AP UE Context Setup Resp (incl. CellGroupConfig) + Note over cucp: rrc_CU_process_ue_context_setup_response() ("on target CU") + Note over cucp: cuup_notify_reestablishment() + cucp->>cuup: E1AP Bearer Context Modification Req + cucp-->>cucp: callback: ho_req_ack() + Note over cucp: nr_rrc_f1_ho_acknowledge() ("on source CU") + cucp->>sdu: F1AP Context Modification Req (RRC Reconfiguration) + cuup->>cucp: E1AP Bearer Context Modification Resp + sdu->>ue: RRC Reconfiguration + sdu->>cucp: F1AP Context Modification Resp + Note over sdu: Stop scheduling UE + Note over ue: Resync + Note over ue,tdu: RA (Msg1 + Msg2) + ue->>tdu: RRC Reconfiguration Complete + tdu->>cucp: F1AP UL RRC Msg Transfer (RRC Reconfiguration Complete) + Note over cucp: handle_rrcReconfigurationComplete() ("on target CU") + cucp-->>cucp: callback: ho_success() + Note over cucp: nr_rrc_f1_ho_complete() ("on source CU") + cucp->>sdu: F1AP UE Context Release Command + sdu->>cucp: F1AP UE Context Release Complete + Note over ue,tdu: UE active on target DU +``` + +# Structures + +## Cells + +OAI 5G RRC does not actually handle multiple cells as of now, but multiple DUs, +each being limited to one cell. + +Cell-related data is stored in `nr_rrc_du_container_t`, and kept in a tree +indexed by the SCTP association ID. + +## CU-UPs + +CU-UP information is stored in `nr_rrc_cuup_container_t`, and kept in a tree +indexed by the SCTP association ID. + +## Transactions + +The RRC keeps track of ongoing transaction (RRC procedures) through a per-UE +array `xids`, which is indexed with a transaction ID `xid` in `[0,3]` to keep +track of ongoing transactions. Upon RRC Reconfiguration Complete, these IDs are +deleted. + +Note that while 38.331 requires only one RRC transaction to happen at a time, +the 5G RRC does not actually ensure this; it only tries to prevent it by +chaining of transactions and some checks. For instance, the Security Mode +procedure is followed by a UE capability procedure, and some procedures are not +executed if others are ongoing (ex.: handover if there is reconfiguratio). +However, it might be possible to trigger a procedure while another is ongoing. + +As of now, no queueing mechanims exists to ensure only one operation is +ongoing, which would likely also simplify the code. + +## Handover + +Handover-related data is stored in a per-UE structure of type +`nr_handover_context_t`. It is a pointer and only set during handover +operation. This data structure has in turn two pointers, one to the source CU +(`nr_ho_source_cu_t`), and one to the target CU (`nr_ho_target_cu_t`). In F1, +both are present. In N2 and Xn, only one pointer is supposed to be set a the +corresponding CU. + +`nr_ho_source_cu_t` contains notably a function pointer `ho_cancel` for +handover cancel. `nr_ho_target_cu_t` contains function pointers `ho_req_ack` +for handover request acknowledge and `ho_success` for handover success. As can +be seen in the sequence diagram above, either the "target CU" or "source CU" +needs to do an operation, and a "switch" from target to source CU is done using +these function pointers. For instance, in F1, the handover request acknowledge +function pointers merely calls another (RRC) function which triggers a +reconfiguration. In the case of N2, the handover request acknowledge function +pointer should trigger the NGAP Handover Request Acknowledge, and the handover +success function pointer should trigger the NGAP Handover Success message. diff --git a/doc/RRC/rrc-usage.md b/doc/RRC/rrc-usage.md new file mode 100644 index 0000000000..668c106a0d --- /dev/null +++ b/doc/RRC/rrc-usage.md @@ -0,0 +1,99 @@ +This document describes the basic functioning of the 5G RRC layer, describes +the periodic output, and explains the various configuration options that +influence its behavior. + +Developer documentation, such as UE connection control flow, reestablishment, or handover, are described in [a separate page](./rrc-dev.md). + +[[_TOC_]] + +# General + +The RRC layer controls the basic connection setup of UEs as well as additional +procedures. It is the fundamental building block of OAI's CU-CP, and interacts +with lower layers (DU, basically MAC and RLC) through F1AP messages, and with +the CU-UP through E1AP messages. More information can be found in the +respective [F1AP page](../F1-design.md) and [E1AP page](../E1AP/E1-design.md). + +# Periodic output and interpretation + +Similarly to the scheduler, the RRC periodically prints information about +connected UEs and DUs into file `nrRRC_stats.log` in the current working +directory of the executable running the RRC (typically, `nr-softmodem`). The +output lists first all UEs that are currently connected, and then all DUs, in +order. + +For each UE, it prints: + +``` +UE 0 CU UE ID 1 DU UE ID 40352 RNTI 9da0 random identity c0f1ac9824000000: + last RRC activity: 5 seconds ago + PDU session 0 ID 10 status established + associated DU: DU assoc ID 8 +``` + +where `UE 0` is the UE index, CU UE ID and DU UE IDs are the IDs used to +exchange over F1 (cf. scheduler logs). Further, it shows RNTI, when the last +RRC activity happened, the status of PDU sessions and which DU is associated +(through the SCTP association ID). + + +For each DU, it prints: + +``` +1 connected DUs +[1] DU ID 3584 (gNB-OAI-DU) assoc_id 8: nrCellID 12345678, PCI 0, SSB ARFCN 641280 + TDD: band 78 ARFCN 640008 SCS 30 (kHz) PRB 106 +``` + +i.e., an index (`[1]`), the DU ID and it's name, the SCTP association ID +(`assoc_id 8`, cf. UE information), and DU specific information for the cell +(cell ID, physical cell identity/PCI, the SSB frequency in ARFCN notation, the +band and Point A ARFCN, subcarrier spacing/SCS, and the number of resource +blocks/PRB). Only one cell per DU is supported. + +As of now, it does not print information about connected CU-UPs or AMFs. + +# Configuration of the RRC + +## Split-related options (when running in a CU or CU-CP) + +See [F1 documentation](../F1-design.md) for information about the F1 split. +See [E1 documentation](../E1AP/E1-design.md) for information about the E1 split. + +## RRC-specific configuration options + +In the `gNBs` section of the gNB/CU/CU-CP configuration file is the +RRC-specific configuration + +### cell-specific options + +- `gNB_ID` and `gNB_name`: ID and name of the gNB +- `tracking_area_code`: the current tracking area code in the range `[0x0001, + 0xfffd]` +- `plmn`: the PLMN, which is a list of entries consisting of: + - `mcc`: mobile country code + - `mnc`: mobile network code + - `mnc_length`: length of mobile network code, allowed values: 2, 3 + - `snssaiList`: list of NSSAI (network selection slice assistence + information, "slice ID"), which itself consists in + - `sst`: slice service type, in `[1,255]` + - `sd` (default `0xffffff`): slice differentiator, in `[0,0xffffff]`, + `0xffffff` is a reserved value and means "no SD" + Note that: SST=1, no SD is "eMBB"; SST=2, no SD is "URLLC"; SST=3, no SD + is "mMTC" +- `enable_sdap` (default: false): enable the use of the SDAP layer. If + deactivated, a transparent SDAP header is prepended to packets, but no + further processing is being done. + +### UE-specific configuration + +- `um_on_default_drb` (default: false): use RLC UM instead of RLC AM on default + bearers +- `drbs` (default: 0): the number of DRBs to allocate for a UE, only useful for + do-ra or phy-test testing + +### Neighbor-gNB configuration + + TBD + +Refer to the [handover tutorial](../handover-tutorial.md) for more information.