Merge remote-tracking branch 'origin/develop-measGapConfig' into integration_2025_w29 (!3225)

Measurement gaps

Scheduling measurement gaps to provide sufficient time for the UE to
re-tune its transceiver to the target carrier, complete the set of
measurements and then re-tune its transceiver back to the original
carrier.
This commit is contained in:
Robert Schmidt
2025-07-17 09:44:35 +02:00
24 changed files with 476 additions and 78 deletions
+5
View File
@@ -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;
+6
View File
@@ -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);
+4
View File
@@ -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)
+2 -2
View File
@@ -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
+1 -1
View File
@@ -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
+1
View File
@@ -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;
+24 -1
View File
@@ -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);
}
+2
View File
@@ -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);
@@ -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) {
@@ -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;
@@ -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);
}
}
}
}
+2
View File
@@ -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,
@@ -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) */
@@ -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;
@@ -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;
+15 -1
View File
@@ -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;
@@ -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);
}
@@ -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
+88 -44
View File
@@ -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;
}
+6 -1
View File
@@ -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);
+2
View File
@@ -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);
+46 -21
View File
@@ -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;
+15
View File
@@ -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);
+1 -2
View File
@@ -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) {