diff --git a/common/utils/nr/nr_common.c b/common/utils/nr/nr_common.c index 5d98a99bb4..7875fe546a 100644 --- a/common/utils/nr/nr_common.c +++ b/common/utils/nr/nr_common.c @@ -1415,6 +1415,11 @@ uint32_t nr_timer_elapsed_time(const NR_timer_t *timer) return timer->counter; } +uint32_t nr_timer_remaining_time(const NR_timer_t *timer) +{ + return timer->target - timer->counter; +} + void nr_timer_setup(NR_timer_t *timer, const uint32_t target, const uint32_t step) { timer->target = target; diff --git a/common/utils/nr/nr_common.h b/common/utils/nr/nr_common.h index f98ce510a2..05ee80c18a 100644 --- a/common/utils/nr/nr_common.h +++ b/common/utils/nr/nr_common.h @@ -222,6 +222,12 @@ bool nr_timer_is_active(const NR_timer_t *timer); * @return Time passed since start of timer */ uint32_t nr_timer_elapsed_time(const NR_timer_t *timer); +/** + * @brief To return how much time is left until the timer expires + * @param timer Timer to be checked + * @return Time left until the timer expires + */ +uint32_t nr_timer_remaining_time(const NR_timer_t *timer); int set_default_nta_offset(frequency_range_t freq_range, uint32_t samples_per_subframe); diff --git a/doc/FEATURE_SET.md b/doc/FEATURE_SET.md index 3c7490d937..9faf2c3d3b 100644 --- a/doc/FEATURE_SET.md +++ b/doc/FEATURE_SET.md @@ -139,6 +139,10 @@ These modes of operation are supported: - evaluation of CQI report - MAC scheduling of SR reception - Intra-frequency handover +- Inter-frequency handover + - Measurement gaps are automatically computed at the DU if the CU has neighbor information and the configured + neighbors include cells operating on different frequencies + - DUs must be synchronized with each other for the measurements to be properly performed - Initial support for RedCap - Scheduling of SIBs (2, 19) diff --git a/doc/RRC/rrc-dev.md b/doc/RRC/rrc-dev.md index d511421b01..e6eb3f97e0 100644 --- a/doc/RRC/rrc-dev.md +++ b/doc/RRC/rrc-dev.md @@ -291,7 +291,7 @@ 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). +executed if others are ongoing (ex.: handover if there is reconfiguration). 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 @@ -303,7 +303,7 @@ 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 +both are present. In N2 and Xn, only one pointer is supposed to be set at the corresponding CU. `nr_ho_source_cu_t` contains notably a function pointer `ho_cancel` for diff --git a/doc/handover-tutorial.md b/doc/handover-tutorial.md index 5fe08be665..87a45ed1e5 100644 --- a/doc/handover-tutorial.md +++ b/doc/handover-tutorial.md @@ -123,7 +123,7 @@ 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 +We support both intra-frequency and inter-frequency handovers. 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 diff --git a/openair2/COMMON/f1ap_messages_types.h b/openair2/COMMON/f1ap_messages_types.h index 89826e7ff5..e44e1dccb4 100644 --- a/openair2/COMMON/f1ap_messages_types.h +++ b/openair2/COMMON/f1ap_messages_types.h @@ -381,6 +381,7 @@ typedef struct f1ap_cu_to_du_rrc_info_s { byte_array_t *cg_configinfo; byte_array_t *ue_cap; byte_array_t *meas_config; + byte_array_t *meas_timing_config; byte_array_t *ho_prep_info; } f1ap_cu_to_du_rrc_info_t; diff --git a/openair2/F1AP/lib/f1ap_ue_context.c b/openair2/F1AP/lib/f1ap_ue_context.c index a1b5531c2b..9d8cbfe15f 100644 --- a/openair2/F1AP/lib/f1ap_ue_context.c +++ b/openair2/F1AP/lib/f1ap_ue_context.c @@ -57,8 +57,9 @@ static F1AP_CUtoDURRCInformation_t encode_cu_to_du_rrc_info(const f1ap_cu_to_du_ } /* optional: HandoverPreparationInformation */ + F1AP_ProtocolExtensionContainer_10696P60_t *p = NULL; if (cu2du->ho_prep_info) { - F1AP_ProtocolExtensionContainer_10696P60_t *p = calloc_or_fail(1, sizeof(*p)); + p = calloc_or_fail(1, sizeof(*p)); enc.iE_Extensions = (struct F1AP_ProtocolExtensionContainer *)p; asn1cSequenceAdd(p->list, F1AP_CUtoDURRCInformation_ExtIEs_t, ie_ext); ie_ext->id = F1AP_ProtocolIE_ID_id_HandoverPreparationInformation; @@ -68,6 +69,20 @@ static F1AP_CUtoDURRCInformation_t encode_cu_to_du_rrc_info(const f1ap_cu_to_du_ F1AP_HandoverPreparationInformation_t *hpi = &ie_ext->extensionValue.choice.HandoverPreparationInformation; OCTET_STRING_fromBuf(hpi, (const char *)ba->buf, ba->len); } + + /* optional: iE_Extensions */ + if (cu2du->meas_timing_config) { + if (!p) + p = calloc_or_fail(1, sizeof(*p)); + enc.iE_Extensions = (struct F1AP_ProtocolExtensionContainer *)p; + asn1cSequenceAdd(p->list, F1AP_CUtoDURRCInformation_ExtIEs_t, ie_ext); + ie_ext->id = F1AP_ProtocolIE_ID_id_MeasurementTimingConfiguration; + ie_ext->criticality = F1AP_Criticality_ignore; + ie_ext->extensionValue.present = F1AP_CUtoDURRCInformation_ExtIEs__extensionValue_PR_MeasurementTimingConfiguration; + const byte_array_t *ba = cu2du->meas_timing_config; + F1AP_MeasurementTimingConfiguration_t *mtc = &ie_ext->extensionValue.choice.MeasurementTimingConfiguration; + OCTET_STRING_fromBuf(mtc, (const char *)ba->buf, ba->len); + } return enc; } @@ -98,6 +113,11 @@ static bool decode_cu_to_du_rrc_info(f1ap_cu_to_du_rrc_info_t *dec, const F1AP_C const F1AP_HandoverPreparationInformation_t *hopi = &cu2du_info_ext->extensionValue.choice.HandoverPreparationInformation; *dec->ho_prep_info = create_byte_array(hopi->size, (uint8_t*) hopi->buf); break; + case F1AP_ProtocolIE_ID_id_MeasurementTimingConfiguration: + dec->meas_timing_config = calloc_or_fail(1, sizeof(*dec->meas_timing_config)); + const F1AP_MeasurementTimingConfiguration_t *mtc = &cu2du_info_ext->extensionValue.choice.MeasurementTimingConfiguration; + *dec->meas_timing_config = create_byte_array(mtc->size, (uint8_t *)mtc->buf); + break; default: PRINT_ERROR("received unsupported F1AP CUtoDURRCInfo extension container %ld\n", cu2du_info_ext->id); } @@ -112,6 +132,7 @@ static f1ap_cu_to_du_rrc_info_t cp_cu_to_du_rrc_info(const f1ap_cu_to_du_rrc_inf CP_OPT_BYTE_ARRAY(dst.cg_configinfo, src->cg_configinfo); CP_OPT_BYTE_ARRAY(dst.ue_cap, src->ue_cap); CP_OPT_BYTE_ARRAY(dst.meas_config, src->meas_config); + CP_OPT_BYTE_ARRAY(dst.meas_timing_config, src->meas_timing_config); CP_OPT_BYTE_ARRAY(dst.ho_prep_info, src->ho_prep_info); return dst; } @@ -126,6 +147,7 @@ static bool eq_cu_to_du_rrc_info(const f1ap_cu_to_du_rrc_info_t *a, const f1ap_c _F1_EQ_CHECK_OPTIONAL_IE(a, b, cg_configinfo, eq_ba); _F1_EQ_CHECK_OPTIONAL_IE(a, b, ue_cap, eq_ba); _F1_EQ_CHECK_OPTIONAL_IE(a, b, meas_config, eq_ba); + _F1_EQ_CHECK_OPTIONAL_IE(a, b, meas_timing_config, eq_ba); _F1_EQ_CHECK_OPTIONAL_IE(a, b, ho_prep_info, eq_ba); return true; } @@ -135,6 +157,7 @@ static void free_cu_to_du_rrc_info(f1ap_cu_to_du_rrc_info_t *cu2du) FREE_OPT_BYTE_ARRAY(cu2du->cg_configinfo); FREE_OPT_BYTE_ARRAY(cu2du->ue_cap); FREE_OPT_BYTE_ARRAY(cu2du->meas_config); + FREE_OPT_BYTE_ARRAY(cu2du->meas_timing_config); FREE_OPT_BYTE_ARRAY(cu2du->ho_prep_info); } diff --git a/openair2/F1AP/tests/f1ap_lib_test.c b/openair2/F1AP/tests/f1ap_lib_test.c index ebf90fc648..72ea809245 100644 --- a/openair2/F1AP/tests/f1ap_lib_test.c +++ b/openair2/F1AP/tests/f1ap_lib_test.c @@ -755,6 +755,7 @@ static void test_f1ap_ue_context_setup_request() orig.cu_to_du_rrc_info.cg_configinfo = get_malloced_test_ba("CG-CONFIGINFO"); orig.cu_to_du_rrc_info.ue_cap = get_malloced_test_ba("UE CAP"); orig.cu_to_du_rrc_info.meas_config = get_malloced_test_ba("UE MEASUREMENT CONFIGURATION"); + orig.cu_to_du_rrc_info.meas_timing_config = get_malloced_test_ba("MEASUREMENT TIMING CONFIGURATION"); orig.cu_to_du_rrc_info.ho_prep_info = get_malloced_test_ba("PREPARATION DATA FOR HANDOVER"); orig.srbs_len = 2; orig.srbs = calloc_or_fail(orig.srbs_len, sizeof(*orig.srbs)); @@ -996,6 +997,7 @@ static void test_f1ap_ue_context_modification_request() .cg_configinfo = get_malloced_test_ba("Modified"), .ue_cap = get_malloced_test_ba("UE forgot to send earlier"), .meas_config = get_malloced_test_ba("with updates from another cell"), + .meas_timing_config = get_malloced_test_ba("measurement timing configuration"), .ho_prep_info = get_malloced_test_ba("prepare to ho soon!"), }; _F1_MALLOC(orig.cu_to_du_rrc_info, cu2du); diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c index 39d75907be..faaa00ea3f 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c @@ -201,6 +201,7 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, frame_t frame, slot_t slo LOG_I(NR_MAC, "Frame.Slot %d.%d\n%s\n", frame, slot, stats_output); } + nr_measgap_scheduling(gNB, frame, slot); nr_mac_update_timers(module_idP, frame, slot); if (wait_prach_completed || get_softmodem_params()->phy_test) { diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index 9c5a00e12c..dad58f52f0 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -628,6 +628,7 @@ static void pf_dl(module_id_t module_id, NR_sched_pdsch_t *sched_pdsch = &sched_ctrl->sched_pdsch; /* get the PID of a HARQ process awaiting retrnasmission, or -1 otherwise */ sched_pdsch->dl_harq_pid = sched_ctrl->retrans_dl_harq.head; + /* Calculate Throughput */ const float a = 0.01f; const uint32_t b = UE->mac_stats.dl.current_bytes; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index 36cb53b8f2..b40329cd69 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -3158,6 +3158,37 @@ void nr_csirs_scheduling(int Mod_idP, frame_t frame, slot_t slot, nfapi_nr_dl_tt } } +void nr_measgap_scheduling(gNB_MAC_INST *nr_mac, frame_t frame, sub_frame_t slot) +{ + NR_SCHED_ENSURE_LOCKED(&nr_mac->sched_lock); + + NR_UEs_t *UE_info = &nr_mac->UE_info; + UE_iterator(UE_info->connected_ue_list, UE) { + measgap_config_t *mgc = &UE->measgap_config; + if (!mgc->enable) + continue; + + const int slots_frame = nr_mac->frame_structure.numb_slots_frame; + const frame_t f = (frame + (slot + mgc->n_slots_advance) / slots_frame) % 1024; + const slot_t s = (slot + mgc->n_slots_advance) % slots_frame; + + // TS 38 331 - Section 5.5.2.9 Measurement gap configuration + if (!(((f % (mgc->mgrp_ms / 10)) == (mgc->gapOffset / 10)) && (s == mgc->gapOffset % 10))) + continue; + + NR_timer_t *t = &UE->UE_sched_ctrl.transm_interrupt; + interrupt_followup_action_t a = nr_timer_is_active(t) ? UE->interrupt_action : FOLLOW_INSYNC; + // start a timer to stop scheduling UE during MeasGap, or extend timer for + // duration of measGap with existing follow-up action + // TODO: if the timer is running, it might be to stop scheduling of the UE afterwards, but then it does not make sense to stop + // scheduling the UE now, we should maybe just skip it? + if (!nr_timer_is_active(t) || nr_timer_remaining_time(t) < mgc->mgl_slots) { + nr_mac_trigger_ul_failure(&UE->UE_sched_ctrl, UE->current_DL_BWP.scs); /* set the UE to "not active" */ + nr_mac_interrupt_ue_transmission(nr_mac, UE, a, mgc->mgl_slots); + } + } +} + void nr_mac_clean_cellgroup(NR_CellGroupConfig_t *cell_group) { DevAssert(cell_group != NULL); @@ -3281,8 +3312,12 @@ void nr_mac_update_timers(module_id_t module_id, frame_t frame, slot_t slot) if (nr_timer_tick(&sched_ctrl->transm_interrupt)) { /* expired */ nr_timer_stop(&sched_ctrl->transm_interrupt); - if (UE->interrupt_action == FOLLOW_OUTOFSYNC) + if (UE->interrupt_action == FOLLOW_OUTOFSYNC) { nr_mac_trigger_ul_failure(sched_ctrl, UE->current_DL_BWP.scs); + } else { + DevAssert(UE->interrupt_action == FOLLOW_INSYNC); + nr_mac_reset_ul_failure(sched_ctrl); + } } } } diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h index c6f9a2f6f9..ea07fbeea4 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h +++ b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h @@ -203,6 +203,8 @@ void nr_csirs_scheduling(int Mod_idP, frame_t frame, slot_t slot, nfapi_nr_dl_tt void nr_csi_meas_reporting(int Mod_idP, frame_t frameP, slot_t slotP); +void nr_measgap_scheduling(gNB_MAC_INST *nr_mac, frame_t frame, sub_frame_t slot); + int nr_acknack_scheduling(gNB_MAC_INST *mac, NR_UE_info_t *UE, frame_t frameP, 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 4af16aa199..b65a829f40 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c +++ b/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c @@ -631,7 +631,9 @@ void ue_context_setup_request(const f1ap_ue_context_setup_req_t *req) } else if (cu2du->ue_cap != NULL) { ue_cap = get_ue_nr_cap(*req->gNB_DU_ue_id, cu2du->ue_cap->buf, cu2du->ue_cap->len); } - AssertFatal(cu2du->meas_config == NULL, "MeasConfig not handled\n"); + NR_MeasurementTimingConfiguration_t *mtc = NULL; + if (cu2du->meas_timing_config != NULL) + mtc = get_nr_mtc(cu2du->meas_timing_config->buf, cu2du->meas_timing_config->len); /* 38.473: "For DC operation, the CG-ConfigInfo IE shall be included in the CU * to DU RRC Information IE at the gNB acting as secondary node" As of now, @@ -703,6 +705,16 @@ void ue_context_setup_request(const f1ap_ue_context_setup_req_t *req) int ss_type = cg_configinfo ? NR_SearchSpace__searchSpaceType_PR_ue_Specific: NR_SearchSpace__searchSpaceType_PR_common; configure_UE_BWP(mac, scc, UE, false, ss_type, -1, -1); + if (mtc) { + /* creates a suitable measGap config to be used in the gNB */ + UE->measgap_config = create_measgap_config(mtc, UE->current_DL_BWP.scs, mac->radio_config.minRXTXTIME); + /* encodes the measGapConfig created, if useful (or not!) */ + byte_array_t *mgc = calloc_or_fail(1, sizeof(*mgc)); + mgc->buf = calloc_or_fail(1, 1024); + mgc->len = encode_measgap_config(&UE->measgap_config, mgc->buf); + resp.du_to_cu_rrc_info.meas_gap_config = mgc; + } + NR_SCHED_UNLOCK(&mac->sched_lock); mac->mac_rrc.ue_context_setup_response(&resp); @@ -727,7 +739,6 @@ void ue_context_modification_request(const f1ap_ue_context_mod_req_t *req) byte_array_t *b = req->cu_to_du_rrc_info->ue_cap; ue_cap = get_ue_nr_cap(req->gNB_DU_ue_id, b->buf, b->len); } - AssertFatal(req->cu_to_du_rrc_info->meas_config == NULL, "MeasConfig not handled\n"); } NR_SCHED_LOCK(&mac->sched_lock); @@ -957,6 +968,7 @@ void dl_rrc_message_transfer(const f1ap_dl_rrc_message_t *dl_rrc) UE->CellGroup = oldUE->CellGroup; oldUE->CellGroup = NULL; UE->mac_stats = oldUE->mac_stats; + UE->measgap_config = oldUE->measgap_config; /* 38.331 5.3.7.2 says that the UE releases the spCellConfig, so we drop it * from the current configuration. It will be reapplied when the * reconfiguration has succeeded (indicated by the CU) */ diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_rrc_ul_direct.c b/openair2/LAYER2/NR_MAC_gNB/mac_rrc_ul_direct.c index cf6c5b2563..deec619e3d 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_rrc_ul_direct.c +++ b/openair2/LAYER2/NR_MAC_gNB/mac_rrc_ul_direct.c @@ -93,7 +93,6 @@ static void ue_context_modification_required_direct(const f1ap_ue_context_modif_ du2cu->cellGroupConfig = malloc(du2cu->cellGroupConfig_length * sizeof(*du2cu->cellGroupConfig)); AssertFatal(du2cu->cellGroupConfig != NULL, "out of memory\n"); memcpy(du2cu->cellGroupConfig, required->du_to_cu_rrc_information->cellGroupConfig, du2cu->cellGroupConfig_length); - AssertFatal(required->du_to_cu_rrc_information->measGapConfig == NULL && required->du_to_cu_rrc_information->measGapConfig_length == 0, "not handled yet\n"); AssertFatal(required->du_to_cu_rrc_information->requestedP_MaxFR1 == NULL && required->du_to_cu_rrc_information->requestedP_MaxFR1_length == 0, "not handled yet\n"); } f1ap_msg->cause = required->cause; 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 19bf8039b6..c7e23ed702 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_rrc_ul_f1ap.c +++ b/openair2/LAYER2/NR_MAC_gNB/mac_rrc_ul_f1ap.c @@ -117,7 +117,6 @@ static void ue_context_modification_required_f1ap(const f1ap_ue_context_modif_re du2cu->cellGroupConfig = malloc(du2cu->cellGroupConfig_length * sizeof(*du2cu->cellGroupConfig)); AssertFatal(du2cu->cellGroupConfig != NULL, "out of memory\n"); memcpy(du2cu->cellGroupConfig, required->du_to_cu_rrc_information->cellGroupConfig, du2cu->cellGroupConfig_length); - AssertFatal(required->du_to_cu_rrc_information->measGapConfig == NULL && required->du_to_cu_rrc_information->measGapConfig_length == 0, "not handled yet\n"); AssertFatal(required->du_to_cu_rrc_information->requestedP_MaxFR1 == NULL && required->du_to_cu_rrc_information->requestedP_MaxFR1_length == 0, "not handled yet\n"); } f1ap_msg->cause = required->cause; diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h index ea2cc617bd..84be6f4b5a 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h +++ b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h @@ -733,7 +733,20 @@ typedef struct nr_mac_rrc_ul_if_s { initial_ul_rrc_message_transfer_func_t initial_ul_rrc_message_transfer; } nr_mac_rrc_ul_if_t; -typedef enum interrupt_followup_action { FOLLOW_INSYNC, FOLLOW_INSYNC_RECONFIG, FOLLOW_OUTOFSYNC } interrupt_followup_action_t; +typedef struct measgap_config { + bool enable; + int mgrp_ms; + long mgrp; + long gapOffset; + long mgta; + int n_slots_mgta; + int n_slots_advance; + float mgl_ms; + long mgl; + int mgl_slots; +} measgap_config_t; + +typedef enum interrupt_followup_action { FOLLOW_INSYNC, FOLLOW_OUTOFSYNC } interrupt_followup_action_t; /*! \brief UE list used by gNB to order UEs/CC for scheduling*/ typedef struct { @@ -753,6 +766,7 @@ typedef struct { NR_SpCellConfig_t *reconfigSpCellConfig; interrupt_followup_action_t interrupt_action; NR_UE_NR_Capability_t *capability; + measgap_config_t measgap_config; // UE selected beam index uint8_t UE_beam_index; float ul_thr_ue; diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c b/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c index a64cd61afb..ad22b9dd63 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c +++ b/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c @@ -4090,3 +4090,198 @@ NR_ReconfigurationWithSync_t *get_reconfiguration_with_sync(rnti_t rnti, uid_t u return reconfigurationWithSync; } + +static NR_MeasGapConfig_t *get_gap_config_from_smtc(const NR_SSB_MTC_t *ssb_mtc) +{ + NR_MeasGapConfig_t *measGapConfig = calloc_or_fail(1, sizeof(*measGapConfig)); + measGapConfig->ext1 = calloc_or_fail(1, sizeof(*measGapConfig->ext1)); + measGapConfig->ext1->gapUE = calloc_or_fail(1, sizeof(*measGapConfig->ext1->gapUE)); + measGapConfig->ext1->gapUE->present = NR_SetupRelease_GapConfig_PR_setup; + NR_GapConfig_t *gap_config = calloc_or_fail(1, sizeof(*gap_config)); + measGapConfig->ext1->gapUE->choice.setup = gap_config; + + // mgta = Measurement Gap Timing Advance, to provide sufficient time for the UE to re-tune its transceiver + // This allows the Measurement Gap to extend mgta ms either side of the SS/PBCH Measurement Window. + gap_config->mgta = NR_GapConfig__mgta_ms0dot5; + + // mgrp = Measurement Gap Repetition Period + // gapOffset = It defines the start of the Measurement Gaps relative to the start of the radio frame with SFN = 0 + // The Measurement Gaps need to be synchronized with the SS/PBCH transmissions which are to be measured + gap_config->mgrp = NR_GapConfig__mgrp_ms160; + switch (ssb_mtc->periodicityAndOffset.present) { + case NR_SSB_MTC__periodicityAndOffset_PR_sf20: + gap_config->gapOffset = ssb_mtc->periodicityAndOffset.choice.sf20; + break; + case NR_SSB_MTC__periodicityAndOffset_PR_sf40: + gap_config->gapOffset = ssb_mtc->periodicityAndOffset.choice.sf40; + break; + case NR_SSB_MTC__periodicityAndOffset_PR_sf80: + gap_config->gapOffset = ssb_mtc->periodicityAndOffset.choice.sf80; + break; + case NR_SSB_MTC__periodicityAndOffset_PR_sf160: + gap_config->gapOffset = ssb_mtc->periodicityAndOffset.choice.sf160; + break; + default: + LOG_W(NR_RRC, "SMTC periodicity higher than MGRP\n"); + // With this configuration, not all SSBs belong to the Measurement Gap. + if (ssb_mtc->periodicityAndOffset.present == NR_SSB_MTC__periodicityAndOffset_PR_sf5) { + gap_config->gapOffset = ssb_mtc->periodicityAndOffset.choice.sf5; + } else if (ssb_mtc->periodicityAndOffset.present == NR_SSB_MTC__periodicityAndOffset_PR_sf10) { + gap_config->gapOffset = ssb_mtc->periodicityAndOffset.choice.sf10; + } + } + + // mgl = Measurement Gap Length + // FIXME: At least the duration of the SMTC plus 2 times the mgta should be enough, + // however, at the moment it only works by setting the maximum value + gap_config->mgl = NR_GapConfig__mgl_ms6; + + return measGapConfig; +} + +NR_MeasurementTimingConfiguration_t *get_nr_mtc(uint8_t *buf, uint32_t len) +{ + if (buf == NULL || len == 0) + return NULL; + + NR_MeasurementTimingConfiguration_t *mtc = NULL; + asn_dec_rval_t dec_rval = uper_decode(NULL, &asn_DEF_NR_MeasurementTimingConfiguration, (void **)&mtc, buf, len, 0, 0); + if (dec_rval.code != RC_OK) { + LOG_E(NR_MAC, "cannot decode NR MeasurementTimingConfiguration, ignoring\n"); + ASN_STRUCT_FREE(asn_DEF_NR_MeasurementTimingConfiguration, mtc); + return NULL; + } + return mtc; +} + +/** @brief return Measurement Gap Repetition Period from ASN.1 config */ +static int get_mgrp(long mgrp) +{ + switch (mgrp) { + case NR_GapConfig__mgrp_ms20: + return 20; + case NR_GapConfig__mgrp_ms40: + return 40; + case NR_GapConfig__mgrp_ms80: + return 80; + case NR_GapConfig__mgrp_ms160: + return 160; + default: + LOG_E(NR_MAC, "Invalid MGRP %ld\n", mgrp); + return -1; + } +} + +/** @brief Return Measurement Gap Length from ASN.1 config */ +static float get_mgl(long mgl) +{ + switch (mgl) { + case NR_GapConfig__mgl_ms1dot5: + return 1.5; + case NR_GapConfig__mgl_ms3: + return 3.0; + case NR_GapConfig__mgl_ms3dot5: + return 3.5; + case NR_GapConfig__mgl_ms4: + return 4; + case NR_GapConfig__mgl_ms5dot5: + return 5.5; + case NR_GapConfig__mgl_ms6: + return 6; + default: + LOG_E(NR_MAC, "Invalid MGL %ld\n", mgl); + return -1; + } +} + +/** @brief Return Measurement Gap Timing Advance, from ASN.1 config */ +static float get_mgta(long mgta) +{ + switch (mgta) { + case NR_GapConfig__mgta_ms0: + return 0.0; + case NR_GapConfig__mgta_ms0dot25: + return 0.25; + case NR_GapConfig__mgta_ms0dot5: + return 0.5; + default: + LOG_E(NR_MAC, "Invalid MGTA %ld\n", mgta); + return -1; + } +} + +/** @brief Return Measurement Gap Configuration, from ASN.1 config */ +const NR_GapConfig_t *get_gap_config(const NR_MeasGapConfig_t *mgc) +{ + if (mgc->gapFR2 && mgc->gapFR2->present == NR_SetupRelease_GapConfig_PR_setup) + return mgc->gapFR2->choice.setup; + + // FR1 case + if (!mgc->ext1) + return NULL; + + const NR_SetupRelease_GapConfig_t *gapUE = mgc->ext1->gapUE; + if (gapUE && gapUE->present == NR_SetupRelease_GapConfig_PR_setup) + return gapUE->choice.setup; + + const NR_SetupRelease_GapConfig_t *gapFR1 = mgc->ext1->gapFR1; + if (gapFR1 && gapFR1->present == NR_SetupRelease_GapConfig_PR_setup) + return gapFR1->choice.setup; + + return NULL; +} + +measgap_config_t create_measgap_config(const NR_MeasurementTimingConfiguration_t *mtc, int scs, int min_rxtxtime) +{ + measgap_config_t mgc = {0}; + const NR_MeasTimingList_t *mtlist = mtc->criticalExtensions.choice.c1->choice.measTimingConf->measTiming; + const NR_MeasTiming_t *mt = mtlist->list.array[0]; + DevAssert(mt != NULL && mt->frequencyAndTiming != NULL); + const struct NR_MeasTiming__frequencyAndTiming *ft = mt->frequencyAndTiming; + const NR_SSB_MTC_t *ssb_mtc = &ft->ssb_MeasurementTimingConfiguration; + NR_MeasGapConfig_t *measGapConfig = get_gap_config_from_smtc(ssb_mtc); + const NR_GapConfig_t *gap_config = get_gap_config(measGapConfig); + if (!gap_config) + return mgc; + + mgc.mgrp_ms = get_mgrp(gap_config->mgrp); + DevAssert(mgc.mgrp_ms != -1); + mgc.mgrp = gap_config->mgrp; + + mgc.gapOffset = gap_config->gapOffset; + mgc.mgta = gap_config->mgta; + mgc.n_slots_mgta = ((int)(10 * get_mgta(gap_config->mgta)) << scs) / 10; + + // We start the timer K2 slots earlier to avoid scheduling feedback PUCCHs inside measGap + // or depending on the current min_rxtxtime earlier + // TS 38.214 - Table 6.1.2.1.1.2 + const int max_k2 = max(3, min_rxtxtime); + + mgc.n_slots_advance = mgc.n_slots_mgta + max_k2; + + mgc.mgl_ms = get_mgl(gap_config->mgl); + DevAssert(mgc.mgl_ms != -1); + mgc.mgl = gap_config->mgl; + mgc.mgl_slots = ((int)(10 * (mgc.mgl_ms + max_k2)) << scs) / 10; + + mgc.enable = true; + return mgc; +} + +int encode_measgap_config(const measgap_config_t *c, uint8_t *buf) +{ + NR_MeasGapConfig_t *measGapConfig = calloc_or_fail(1, sizeof(*measGapConfig)); + measGapConfig->ext1 = calloc_or_fail(1, sizeof(*measGapConfig->ext1)); + measGapConfig->ext1->gapUE = calloc_or_fail(1, sizeof(*measGapConfig->ext1->gapUE)); + measGapConfig->ext1->gapUE->present = NR_SetupRelease_GapConfig_PR_setup; + NR_GapConfig_t *gap_config = calloc_or_fail(1, sizeof(*gap_config)); + measGapConfig->ext1->gapUE->choice.setup = gap_config; + gap_config->mgta = c->mgta; + gap_config->mgrp = c->mgrp; + gap_config->gapOffset = c->gapOffset; + gap_config->mgl = c->mgl; + asn_enc_rval_t enc_rval_mgc = uper_encode_to_buffer(&asn_DEF_NR_MeasGapConfig, NULL, measGapConfig, buf, 1024); + AssertFatal(enc_rval_mgc.encoded > 0, "Could not encode CellGroup, failed element %s\n", enc_rval_mgc.failed_type->name); + ASN_STRUCT_FREE(asn_DEF_NR_MeasGapConfig, measGapConfig); + return (int)((enc_rval_mgc.encoded + 7) >> 3); +} diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.h b/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.h index bdf56a3310..51c30c38b6 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.h +++ b/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.h @@ -28,6 +28,8 @@ #include "NR_BCCH-DL-SCH-Message.h" #include "NR_CellGroupConfig.h" #include "NR_UE-NR-Capability.h" +#include "NR_MeasConfig.h" +#include "NR_MeasurementTimingConfiguration.h" #include "NR_UL-CCCH-Message.h" #include "f1ap_messages_types.h" #include "common/platform_types.h" @@ -42,6 +44,9 @@ typedef struct nr_mac_config_t nr_mac_config_t; struct nr_mac_timers; typedef struct nr_mac_timers nr_mac_timers_t; +struct measgap_config; +typedef struct measgap_config measgap_config_t; + void nr_rrc_config_dl_tda(struct NR_PDSCH_TimeDomainResourceAllocationList *pdsch_TimeDomainAllocationList, frame_type_t frame_type, NR_TDD_UL_DL_ConfigCommon_t *tdd_UL_DL_ConfigurationCommon, @@ -119,4 +124,8 @@ NR_RLC_BearerConfig_t *get_DRB_RLC_BearerConfig(long lcChannelId, long priority, const nr_rlc_configuration_t *default_rlc_config); +NR_MeasurementTimingConfiguration_t *get_nr_mtc(uint8_t *buf, uint32_t len); +measgap_config_t create_measgap_config(const NR_MeasurementTimingConfiguration_t *mtc, int scs, int min_rxtxtime); +int encode_measgap_config(const measgap_config_t *c, uint8_t *buf); + #endif diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.c b/openair2/RRC/NR/MESSAGES/asn1_msg.c index 0f7ec6db1a..c016e4c899 100644 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.c +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.c @@ -956,23 +956,59 @@ int do_RRCReestablishmentComplete(uint8_t *buffer, size_t buffer_size, int64_t r return((enc_rval.encoded+7)/8); } +static NR_MeasObjectToAddMod_t *get_MeasObject(const struct NR_MeasTiming__frequencyAndTiming *ft, + int band, + NR_ARFCN_ValueNR_t ssbFrequency, + NR_MeasObjectId_t measObjectId) +{ + const NR_SSB_MTC_t *ssb_mtc = &ft->ssb_MeasurementTimingConfiguration; + NR_MeasObjectToAddMod_t *mo = calloc_or_fail(1, sizeof(*mo)); + mo->measObjectId = measObjectId; + mo->measObject.present = NR_MeasObjectToAddMod__measObject_PR_measObjectNR; + NR_MeasObjectNR_t *monr = calloc_or_fail(1, sizeof(*monr)); + asn1cCallocOne(monr->ssbFrequency, ssbFrequency); + asn1cCallocOne(monr->ssbSubcarrierSpacing, ft->ssbSubcarrierSpacing); + monr->referenceSignalConfig.ssb_ConfigMobility = calloc_or_fail(1, sizeof(*monr->referenceSignalConfig.ssb_ConfigMobility)); + monr->referenceSignalConfig.ssb_ConfigMobility->deriveSSB_IndexFromCell = true; + monr->absThreshSS_BlocksConsolidation = calloc_or_fail(1, sizeof(*monr->absThreshSS_BlocksConsolidation)); + asn1cCallocOne(monr->absThreshSS_BlocksConsolidation->thresholdRSRP, 36); + asn1cCallocOne(monr->nrofSS_BlocksToAverage, 8); + monr->smtc1 = calloc_or_fail(1, sizeof(*monr->smtc1)); + monr->smtc1->periodicityAndOffset = ssb_mtc->periodicityAndOffset; + monr->smtc1->duration = ssb_mtc->duration; + monr->quantityConfigIndex = 1; + monr->ext1 = calloc_or_fail(1, sizeof(*monr->ext1)); + asn1cCallocOne(monr->ext1->freqBandIndicatorNR, band); + mo->measObject.choice.measObjectNR = monr; + return mo; +} + +static NR_MeasIdToAddMod_t *get_MeasId(NR_MeasId_t measId, NR_ReportConfigId_t reportConfigId, NR_MeasObjectId_t measObjectId) +{ + NR_MeasIdToAddMod_t *measid = calloc_or_fail(1, sizeof(NR_MeasIdToAddMod_t)); + measid->measId = measId; + measid->reportConfigId = reportConfigId; + measid->measObjectId = measObjectId; + return measid; +} + NR_MeasConfig_t *get_MeasConfig(const NR_MeasTiming_t *mt, int band, int scs, NR_ReportConfigToAddMod_t *rc_PER, NR_ReportConfigToAddMod_t *rc_A2, seq_arr_t *rc_A3_seq, - seq_arr_t *pci_seq) + seq_arr_t *neigh_seq) { - NR_MeasConfig_t *mc = calloc(1, sizeof(*mc)); - mc->measObjectToAddModList = calloc(1, sizeof(*mc->measObjectToAddModList)); - mc->reportConfigToAddModList = calloc(1, sizeof(*mc->reportConfigToAddModList)); - mc->measIdToAddModList = calloc(1, sizeof(*mc->measIdToAddModList)); + NR_MeasConfig_t *mc = calloc_or_fail(1, sizeof(*mc)); + mc->measObjectToAddModList = calloc_or_fail(1, sizeof(*mc->measObjectToAddModList)); + mc->reportConfigToAddModList = calloc_or_fail(1, sizeof(*mc->reportConfigToAddModList)); + mc->measIdToAddModList = calloc_or_fail(1, sizeof(*mc->measIdToAddModList)); if (rc_PER) asn1cSeqAdd(&mc->reportConfigToAddModList->list, rc_PER); - if (rc_A2) + if (rc_A2) asn1cSeqAdd(&mc->reportConfigToAddModList->list, rc_A2); if (rc_A3_seq) { @@ -984,57 +1020,51 @@ NR_MeasConfig_t *get_MeasConfig(const NR_MeasTiming_t *mt, DevAssert(mt != NULL && mt->frequencyAndTiming != NULL); const struct NR_MeasTiming__frequencyAndTiming *ft = mt->frequencyAndTiming; - const NR_SSB_MTC_t *ssb_mtc = &ft->ssb_MeasurementTimingConfiguration; // Measurement Objects: Specifies what is to be measured. For NR and inter-RAT E-UTRA measurements, this may include // cell-specific offsets, blacklisted cells to be ignored and whitelisted cells to consider for measurements. - NR_MeasObjectToAddMod_t *mo1 = calloc(1, sizeof(*mo1)); - mo1->measObjectId = 1; - mo1->measObject.present = NR_MeasObjectToAddMod__measObject_PR_measObjectNR; - NR_MeasObjectNR_t *monr1 = calloc(1, sizeof(*monr1)); - asn1cCallocOne(monr1->ssbFrequency, ft->carrierFreq); - asn1cCallocOne(monr1->ssbSubcarrierSpacing, ft->ssbSubcarrierSpacing); - monr1->referenceSignalConfig.ssb_ConfigMobility = calloc(1, sizeof(*monr1->referenceSignalConfig.ssb_ConfigMobility)); - monr1->referenceSignalConfig.ssb_ConfigMobility->deriveSSB_IndexFromCell = true; - monr1->absThreshSS_BlocksConsolidation = calloc(1, sizeof(*monr1->absThreshSS_BlocksConsolidation)); - asn1cCallocOne(monr1->absThreshSS_BlocksConsolidation->thresholdRSRP, 36); - asn1cCallocOne(monr1->nrofSS_BlocksToAverage, 8); - monr1->smtc1 = calloc(1, sizeof(*monr1->smtc1)); - monr1->smtc1->periodicityAndOffset = ssb_mtc->periodicityAndOffset; - monr1->smtc1->duration = ssb_mtc->duration; - monr1->quantityConfigIndex = 1; - monr1->ext1 = calloc(1, sizeof(*monr1->ext1)); - asn1cCallocOne(monr1->ext1->freqBandIndicatorNR, band); - - if (pci_seq) { - for (int i = 0; i < pci_seq->size; i++) { - if (monr1->cellsToAddModList == NULL) { - monr1->cellsToAddModList = calloc(1, sizeof(*monr1->cellsToAddModList)); - } - - NR_CellsToAddMod_t *cell = calloc(1, sizeof(*cell)); - int *pci = (int *)seq_arr_at(pci_seq, i); - cell->physCellId = *pci; + NR_MeasObjectToAddMod_t *mo1 = get_MeasObject(ft, band, ft->carrierFreq, 1); + if (neigh_seq) { + NR_MeasObjectNR_t *monr1 = mo1->measObject.choice.measObjectNR; + if (monr1->cellsToAddModList == NULL) + monr1->cellsToAddModList = calloc_or_fail(1, sizeof(*monr1->cellsToAddModList)); + FOR_EACH_SEQ_ARR(nr_neighbour_cell_t *, n, neigh_seq) { + NR_CellsToAddMod_t *cell = calloc_or_fail(1, sizeof(*cell)); + cell->physCellId = n->physicalCellId; ASN_SEQUENCE_ADD(&monr1->cellsToAddModList->list, cell); } } - - mo1->measObject.choice.measObjectNR = monr1; asn1cSeqAdd(&mc->measObjectToAddModList->list, mo1); // Preparation of measId - for (uint8_t reportIdx = 0; reportIdx < mc->reportConfigToAddModList->list.count; reportIdx++) { + uint8_t reportIdx = 0; + for (; reportIdx < mc->reportConfigToAddModList->list.count; reportIdx++) { const NR_ReportConfigId_t reportId = mc->reportConfigToAddModList->list.array[reportIdx]->reportConfigId; - NR_MeasIdToAddMod_t *measid = calloc(1, sizeof(NR_MeasIdToAddMod_t)); - measid->measId = reportIdx + 1; - measid->reportConfigId = reportId; - measid->measObjectId = 1; + NR_MeasIdToAddMod_t *measid = get_MeasId(reportIdx + 1, reportId, 1); asn1cSeqAdd(&mc->measIdToAddModList->list, measid); } - mc->quantityConfig = calloc(1, sizeof(*mc->quantityConfig)); - mc->quantityConfig->quantityConfigNR_List = calloc(1, sizeof(*mc->quantityConfig->quantityConfigNR_List)); - NR_QuantityConfigNR_t *qcnr = calloc(1, sizeof(*qcnr)); + // Preparation of measId for neighbour cells for periodic report + if (neigh_seq) { + int mo_id = 2; + FOR_EACH_SEQ_ARR(nr_neighbour_cell_t *, neigh_cell, neigh_seq) { + NR_MeasObjectToAddMod_t *mo_neighbour = get_MeasObject(ft, band, neigh_cell->absoluteFrequencySSB, mo_id); + NR_MeasObjectNR_t *monr = mo_neighbour->measObject.choice.measObjectNR; + monr->cellsToAddModList = calloc_or_fail(1, sizeof(*monr->cellsToAddModList)); + NR_CellsToAddMod_t *cell = calloc_or_fail(1, sizeof(*cell)); + cell->physCellId = neigh_cell->physicalCellId; + ASN_SEQUENCE_ADD(&monr->cellsToAddModList->list, cell); + asn1cSeqAdd(&mc->measObjectToAddModList->list, mo_neighbour); + NR_MeasIdToAddMod_t *measid = get_MeasId(reportIdx + 1, rc_PER->reportConfigId, mo_id); + asn1cSeqAdd(&mc->measIdToAddModList->list, measid); + reportIdx++; + mo_id++; + } + } + + mc->quantityConfig = calloc_or_fail(1, sizeof(*mc->quantityConfig)); + mc->quantityConfig->quantityConfigNR_List = calloc_or_fail(1, sizeof(*mc->quantityConfig->quantityConfigNR_List)); + NR_QuantityConfigNR_t *qcnr = calloc_or_fail(1, sizeof(*qcnr)); asn1cCallocOne(qcnr->quantityConfigCell.ssb_FilterConfig.filterCoefficientRSRP, NR_FilterCoefficient_fc6); asn1cCallocOne(qcnr->quantityConfigCell.csi_RS_FilterConfig.filterCoefficientRSRP, NR_FilterCoefficient_fc6); asn1cSeqAdd(&mc->quantityConfig->quantityConfigNR_List->list, qcnr); @@ -1122,3 +1152,17 @@ int do_NR_HandoverPreparationInformation(const uint8_t *uecap_buf, int uecap_buf ASN_STRUCT_FREE(asn_DEF_NR_HandoverPreparationInformation, hpi); return (enc_rval.encoded + 7) / 8; } + +int do_NR_MeasConfig(const NR_MeasConfig_t *measconfig, uint8_t *buf, int buf_size) +{ + asn_enc_rval_t enc_rval = uper_encode_to_buffer(&asn_DEF_NR_MeasConfig, NULL, measconfig, buf, buf_size); + AssertFatal(enc_rval.encoded > 0, "ASN1 message encoding failed (%s, %lu)!\n", enc_rval.failed_type->name, enc_rval.encoded); + return (enc_rval.encoded + 7) / 8; +} + +int do_NR_MeasurementTimingConfiguration(const NR_MeasurementTimingConfiguration_t *mtc, uint8_t *buf, int buf_size) +{ + asn_enc_rval_t enc_rval = uper_encode_to_buffer(&asn_DEF_NR_MeasurementTimingConfiguration, NULL, mtc, buf, buf_size); + AssertFatal(enc_rval.encoded > 0, "ASN1 message encoding failed (%s, %lu)!\n", enc_rval.failed_type->name, enc_rval.encoded); + 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 3ac4695eba..432db0e5cd 100644 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.h +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.h @@ -50,6 +50,7 @@ #include "NR_ReestablishmentCause.h" #include "NR_SRB-ToAddModList.h" #include "NR_SecurityConfig.h" +#include "NR_MeasurementTimingConfiguration.h" #include "ds/seq_arr.h" #include "ds/byte_array.h" #include "rrc_messages_types.h" @@ -120,6 +121,10 @@ int do_RRCSetupComplete(uint8_t *buffer, int do_NR_HandoverPreparationInformation(const uint8_t *uecap_buf, int uecap_buf_size, uint8_t *buf, int buf_size); +int do_NR_MeasConfig(const NR_MeasConfig_t *measconfig, uint8_t *buf, int buf_size); + +int do_NR_MeasurementTimingConfiguration(const NR_MeasurementTimingConfiguration_t *mtc, uint8_t *buf, int buf_size); + int do_RRCSetupRequest(uint8_t *buffer, size_t buffer_size, uint8_t *rv, uint64_t fiveG_S_TMSI_part1); int do_NR_RRCReconfigurationComplete_for_nsa(uint8_t *buffer, size_t buffer_size, NR_RRC_TransactionIdentifier_t Transaction_id); @@ -151,7 +156,7 @@ NR_MeasConfig_t *get_MeasConfig(const NR_MeasTiming_t *mt, NR_ReportConfigToAddMod_t *rc_PER, NR_ReportConfigToAddMod_t *rc_A2, seq_arr_t *rc_A3_seq, - seq_arr_t *pci_seq); + seq_arr_t *neigh_seq); void free_MeasConfig(NR_MeasConfig_t *mc); int do_NR_Paging(uint8_t Mod_id, uint8_t *buffer, uint32_t tmsi); diff --git a/openair2/RRC/NR/nr_rrc_proto.h b/openair2/RRC/NR/nr_rrc_proto.h index 094f1791c1..0d675ff525 100644 --- a/openair2/RRC/NR/nr_rrc_proto.h +++ b/openair2/RRC/NR/nr_rrc_proto.h @@ -72,6 +72,8 @@ void rrc_gNB_generate_dedicatedRRCReconfiguration_release(gNB_RRC_INST *rrc, uint32_t nas_length, uint8_t *nas_buffer); +NR_MeasConfig_t *nr_rrc_get_measconfig(const gNB_RRC_INST *rrc, uint64_t nr_cellid); + bool ue_associated_to_cuup(const gNB_RRC_INST *rrc, const gNB_RRC_UE_t *ue); sctp_assoc_t get_existing_cuup_for_ue(const gNB_RRC_INST *rrc, const gNB_RRC_UE_t *ue); sctp_assoc_t get_new_cuup_for_ue(const gNB_RRC_INST *rrc, const gNB_RRC_UE_t *ue, int sst, int sd); diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index ae4d55cae7..75f6838a78 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -585,18 +585,14 @@ void free_RRCReconfiguration_params(nr_rrc_reconfig_param_t params) FREE_AND_ZERO_BYTE_ARRAY(params.dedicated_NAS_msg_list[i]); } -/** @brief Prepare the instance of RRCReconfigurationParams to be pass to RRC encoding */ -nr_rrc_reconfig_param_t get_RRCReconfiguration_params(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, uint8_t srb_reest_bitmap, bool drb_reestablish) +NR_MeasConfig_t *nr_rrc_get_measconfig(const gNB_RRC_INST *rrc, uint64_t nr_cellid) { - uint8_t xid = rrc_gNB_get_next_transaction_identifier(rrc->module_id); - nr_rrc_du_container_t *du = get_du_for_ue(rrc, UE->rrc_ue_id); + nr_rrc_du_container_t *du = get_du_by_cell_id((gNB_RRC_INST *)rrc, nr_cellid); DevAssert(du != NULL); f1ap_served_cell_info_t *cell_info = &du->setup_req->cell[0].info; - NR_MeasConfig_t *measconfig = NULL; NR_ReportConfigToAddMod_t *rc_PER = NULL; NR_ReportConfigToAddMod_t *rc_A2 = NULL; seq_arr_t *rc_A3_seq = NULL; - seq_arr_t *pci_seq = NULL; if (du->mtc != NULL) { int scs = get_ssb_scs(cell_info); int band = get_dl_band(cell_info); @@ -607,25 +603,18 @@ nr_rrc_reconfig_param_t get_RRCReconfiguration_params(gNB_RRC_INST *rrc, gNB_RRC if (neighbour_config) neighbour_cells = neighbour_config->neighbour_cells; - if (neighbour_cells - && rrc->measurementConfiguration.a3_event_list - && rrc->measurementConfiguration.a3_event_list->size > 0) { + if (neighbour_cells && rrc->measurementConfiguration.a3_event_list && rrc->measurementConfiguration.a3_event_list->size > 0) { /* Loop through neighbours and find related A3 configuration If no related A3 but there is default add the default one. If default one added once as a report, no need to add it again && duplication. */ rc_A3_seq = malloc(sizeof(seq_arr_t)); - pci_seq = malloc(sizeof(seq_arr_t)); seq_arr_init(rc_A3_seq, sizeof(NR_ReportConfigToAddMod_t)); - seq_arr_init(pci_seq, sizeof(int)); LOG_D(NR_RRC, "HO LOG: Preparing A3 Event Measurement Configuration!\n"); bool is_default_a3_added = false; for (int i = 0; i < neighbour_cells->size; i++) { nr_neighbour_cell_t *neighbourCell = (nr_neighbour_cell_t *)seq_arr_at(neighbour_cells, i); - if (!neighbourCell->isIntraFrequencyNeighbour) - continue; - seq_arr_push_back(pci_seq, &neighbourCell->physicalCellId, sizeof(int)); - const nr_a3_event_t *a3Event = get_a3_configuration(rrc, neighbourCell->physicalCellId); + const nr_a3_event_t *a3Event = get_a3_configuration((gNB_RRC_INST *)rrc, neighbourCell->physicalCellId); if (!a3Event || is_default_a3_added) continue; if (a3Event->pci == -1) @@ -637,14 +626,15 @@ nr_rrc_reconfig_param_t get_RRCReconfiguration_params(gNB_RRC_INST *rrc, gNB_RRC rc_PER = prepare_periodic_event_report(rrc->measurementConfiguration.per_event); if (rrc->measurementConfiguration.a2_event) rc_A2 = prepare_a2_event_report(rrc->measurementConfiguration.a2_event); - - measconfig = get_MeasConfig(mt, band, scs, rc_PER, rc_A2, rc_A3_seq, pci_seq); + return get_MeasConfig(mt, band, scs, rc_PER, rc_A2, rc_A3_seq, neighbour_cells); } + return NULL; +} - if (UE->measConfig) - free_MeasConfig(UE->measConfig); - - UE->measConfig = measconfig; +/** @brief Prepare the instance of RRCReconfigurationParams to be pass to RRC encoding */ +nr_rrc_reconfig_param_t get_RRCReconfiguration_params(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, uint8_t srb_reest_bitmap, bool drb_reestablish) +{ + uint8_t xid = rrc_gNB_get_next_transaction_identifier(rrc->module_id); // Re-establish PDCP for SRB2 only NR_SRB_ToAddModList_t *SRBs = createSRBlist(UE, srb_reest_bitmap); @@ -1070,6 +1060,7 @@ static void rrc_gNB_process_RRCReestablishmentComplete(gNB_RRC_INST *rrc, gNB_RR uint8_t new_xid = rrc_gNB_get_next_transaction_identifier(rrc->module_id); ue_p->xids[new_xid] = RRC_REESTABLISH_COMPLETE; nr_rrc_reconfig_param_t params = {.cell_group_config = cellGroupConfig, + .meas_config = ue_p->measConfig, .drb_config_list = DRBs, .srb_config_list = SRBs, .transaction_id = new_xid}; @@ -1189,6 +1180,7 @@ static void rrc_handle_RRCSetupRequest(gNB_RRC_INST *rrc, UE->nr_cellid = msg->nr_cellid; UE->masterCellGroup = cellGroupConfig; UE->ongoing_reconfiguration = false; + UE->measConfig = nr_rrc_get_measconfig(rrc, UE->nr_cellid); activate_srb(UE, 1); rrc_gNB_generate_RRCSetup(0, msg->crnti, ue_context_p, msg->du2cu_rrc_container, msg->du2cu_rrc_container_length); } @@ -2137,6 +2129,17 @@ static void rrc_CU_process_ue_context_setup_response(MessageDef *msg_p, instance uper_decode_complete(NULL, &asn_DEF_NR_CellGroupConfig, (void **)&cellGroupConfig, (uint8_t *)cgc->buf, cgc->len); AssertFatal(dec_rval.code == RC_OK && dec_rval.consumed > 0, "Cell group config decode error\n"); + if (resp->du_to_cu_rrc_info.meas_gap_config) { + NR_MeasGapConfig_t *measGapConfig = NULL; + asn_dec_rval_t dec_rval_mgc = uper_decode_complete(NULL, + &asn_DEF_NR_MeasGapConfig, + (void **)&measGapConfig, + (uint8_t *)resp->du_to_cu_rrc_info.meas_gap_config->buf, + resp->du_to_cu_rrc_info.meas_gap_config->len); + AssertFatal(dec_rval_mgc.code == RC_OK && dec_rval_mgc.consumed > 0, "measGapConfig decode error\n"); + UE->measConfig->measGapConfig = measGapConfig; + } + if (UE->masterCellGroup) { ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, UE->masterCellGroup); LOG_I(RRC, "UE %04x replacing existing CellGroupConfig with new one received from DU\n", UE->rnti); @@ -3087,6 +3090,28 @@ void rrc_gNB_generate_UeContextSetupRequest(const gNB_RRC_INST *rrc, *cu2du.ue_cap = copy_byte_array(ue_p->ue_cap_buffer); } + nr_rrc_du_container_t *du = get_du_for_ue((gNB_RRC_INST *)rrc, ue_p->rrc_ue_id); + byte_array_t *meas_timing_config = NULL; + if (du->mtc && ue_p->measConfig && ue_p->measConfig->measObjectToAddModList) { + NR_MeasObjectToAddModList_t *mo_list = ue_p->measConfig->measObjectToAddModList; + NR_ARFCN_ValueNR_t ssbFrequency0; + for (int i = 0; i < mo_list->list.count; i++) { + NR_MeasObjectToAddMod_t *mo = mo_list->list.array[i]; + if (mo->measObject.present == NR_MeasObjectToAddMod__measObject_PR_measObjectNR) { + NR_MeasObjectNR_t *monr = mo->measObject.choice.measObjectNR; + if (i == 0) { + ssbFrequency0 = *monr->ssbFrequency; + } else if (ssbFrequency0 != *monr->ssbFrequency) { + meas_timing_config = calloc_or_fail(1, sizeof(*meas_timing_config)); + meas_timing_config->buf = calloc_or_fail(1, NR_RRC_BUF_SIZE); + meas_timing_config->len = do_NR_MeasurementTimingConfiguration(du->mtc, meas_timing_config->buf, NR_RRC_BUF_SIZE); + cu2du.meas_timing_config = meas_timing_config; + break; + } + } + } + } + int nb_srb = 1; f1ap_srb_to_setup_t *srbs = calloc_or_fail(nb_srb, sizeof(*srbs)); srbs[0].id = 2; diff --git a/openair2/RRC/NR/rrc_gNB_mobility.c b/openair2/RRC/NR/rrc_gNB_mobility.c index 85c9597371..fe3214da2d 100644 --- a/openair2/RRC/NR/rrc_gNB_mobility.c +++ b/openair2/RRC/NR/rrc_gNB_mobility.c @@ -181,6 +181,19 @@ static void nr_initiate_handover(const gNB_RRC_INST *rrc, srbs[0].id = 1; srbs[1].id = 2; + free_MeasConfig(ue->measConfig); + ue->measConfig = nr_rrc_get_measconfig(rrc, target_du->setup_req->cell[0].info.nr_cellid); + byte_array_t *meas_config = calloc_or_fail(1, sizeof(*meas_config)); + meas_config->buf = calloc_or_fail(1, NR_RRC_BUF_SIZE); + meas_config->len = do_NR_MeasConfig(ue->measConfig, meas_config->buf, NR_RRC_BUF_SIZE); + + byte_array_t *meas_timing_config = NULL; + if (target_du->mtc) { + meas_timing_config = calloc_or_fail(1, sizeof(*meas_timing_config)); + meas_timing_config->buf = calloc_or_fail(1, NR_RRC_BUF_SIZE); + meas_timing_config->len = do_NR_MeasurementTimingConfiguration(target_du->mtc, meas_timing_config->buf, NR_RRC_BUF_SIZE); + } + byte_array_t *hpi = malloc_or_fail(sizeof(*hpi)); *hpi = copy_byte_array(*ho_prep_info); @@ -201,6 +214,8 @@ static void nr_initiate_handover(const gNB_RRC_INST *rrc, .drbs_len = nb_drb, .drbs = drbs, .cu_to_du_rrc_info.ho_prep_info = hpi, + .cu_to_du_rrc_info.meas_config = meas_config, + .cu_to_du_rrc_info.meas_timing_config = meas_timing_config, .gnb_du_ue_agg_mbr_ul = ue_agg_mbr, }; rrc->mac_rrc.ue_context_setup_request(target_du->assoc_id, &ue_context_setup_req); diff --git a/openair2/RRC/NR_UE/rrc_UE.c b/openair2/RRC/NR_UE/rrc_UE.c index da1b3d8ba0..87cb68303a 100644 --- a/openair2/RRC/NR_UE/rrc_UE.c +++ b/openair2/RRC/NR_UE/rrc_UE.c @@ -1871,8 +1871,7 @@ static void nr_rrc_ue_process_measConfig(rrcPerNB_t *rrc, NR_MeasConfig_t *const if (measConfig->measIdToAddModList) handle_measid_addmod(rrc, measConfig->measIdToAddModList, timers); - AssertFatal(!measConfig->measGapConfig, "Measurement gaps not yet supported\n"); - AssertFatal(!measConfig->measGapSharingConfig, "Measurement gaps not yet supported\n"); + LOG_W(NR_RRC, "Measurement gaps not yet supported!\n"); if (measConfig->s_MeasureConfig) { if (measConfig->s_MeasureConfig->present == NR_MeasConfig__s_MeasureConfig_PR_ssb_RSRP) {