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:
@@ -1415,6 +1415,11 @@ uint32_t nr_timer_elapsed_time(const NR_timer_t *timer)
|
|||||||
return timer->counter;
|
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)
|
void nr_timer_setup(NR_timer_t *timer, const uint32_t target, const uint32_t step)
|
||||||
{
|
{
|
||||||
timer->target = target;
|
timer->target = target;
|
||||||
|
|||||||
@@ -222,6 +222,12 @@ bool nr_timer_is_active(const NR_timer_t *timer);
|
|||||||
* @return Time passed since start of timer
|
* @return Time passed since start of timer
|
||||||
*/
|
*/
|
||||||
uint32_t nr_timer_elapsed_time(const NR_timer_t *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);
|
int set_default_nta_offset(frequency_range_t freq_range, uint32_t samples_per_subframe);
|
||||||
|
|
||||||
|
|||||||
@@ -139,6 +139,10 @@ These modes of operation are supported:
|
|||||||
- evaluation of CQI report
|
- evaluation of CQI report
|
||||||
- MAC scheduling of SR reception
|
- MAC scheduling of SR reception
|
||||||
- Intra-frequency handover
|
- 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
|
- Initial support for RedCap
|
||||||
- Scheduling of SIBs (2, 19)
|
- Scheduling of SIBs (2, 19)
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -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
|
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
|
chaining of transactions and some checks. For instance, the Security Mode
|
||||||
procedure is followed by a UE capability procedure, and some procedures are not
|
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.
|
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
|
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
|
`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
|
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,
|
(`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.
|
corresponding CU.
|
||||||
|
|
||||||
`nr_ho_source_cu_t` contains notably a function pointer `ho_cancel` for
|
`nr_ho_source_cu_t` contains notably a function pointer `ho_cancel` for
|
||||||
|
|||||||
@@ -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
|
separated by at least multiple meters to ensure that the UE will receive
|
||||||
different signal strengths when moving between cells.
|
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.
|
only, although other radios should work as well.
|
||||||
|
|
||||||
For UEs, we verified Quectel modules and iPhones. Note, though, that not all
|
For UEs, we verified Quectel modules and iPhones. Note, though, that not all
|
||||||
|
|||||||
@@ -381,6 +381,7 @@ typedef struct f1ap_cu_to_du_rrc_info_s {
|
|||||||
byte_array_t *cg_configinfo;
|
byte_array_t *cg_configinfo;
|
||||||
byte_array_t *ue_cap;
|
byte_array_t *ue_cap;
|
||||||
byte_array_t *meas_config;
|
byte_array_t *meas_config;
|
||||||
|
byte_array_t *meas_timing_config;
|
||||||
byte_array_t *ho_prep_info;
|
byte_array_t *ho_prep_info;
|
||||||
} f1ap_cu_to_du_rrc_info_t;
|
} f1ap_cu_to_du_rrc_info_t;
|
||||||
|
|
||||||
|
|||||||
@@ -57,8 +57,9 @@ static F1AP_CUtoDURRCInformation_t encode_cu_to_du_rrc_info(const f1ap_cu_to_du_
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* optional: HandoverPreparationInformation */
|
/* optional: HandoverPreparationInformation */
|
||||||
|
F1AP_ProtocolExtensionContainer_10696P60_t *p = NULL;
|
||||||
if (cu2du->ho_prep_info) {
|
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;
|
enc.iE_Extensions = (struct F1AP_ProtocolExtensionContainer *)p;
|
||||||
asn1cSequenceAdd(p->list, F1AP_CUtoDURRCInformation_ExtIEs_t, ie_ext);
|
asn1cSequenceAdd(p->list, F1AP_CUtoDURRCInformation_ExtIEs_t, ie_ext);
|
||||||
ie_ext->id = F1AP_ProtocolIE_ID_id_HandoverPreparationInformation;
|
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;
|
F1AP_HandoverPreparationInformation_t *hpi = &ie_ext->extensionValue.choice.HandoverPreparationInformation;
|
||||||
OCTET_STRING_fromBuf(hpi, (const char *)ba->buf, ba->len);
|
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;
|
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;
|
const F1AP_HandoverPreparationInformation_t *hopi = &cu2du_info_ext->extensionValue.choice.HandoverPreparationInformation;
|
||||||
*dec->ho_prep_info = create_byte_array(hopi->size, (uint8_t*) hopi->buf);
|
*dec->ho_prep_info = create_byte_array(hopi->size, (uint8_t*) hopi->buf);
|
||||||
break;
|
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:
|
default:
|
||||||
PRINT_ERROR("received unsupported F1AP CUtoDURRCInfo extension container %ld\n", cu2du_info_ext->id);
|
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.cg_configinfo, src->cg_configinfo);
|
||||||
CP_OPT_BYTE_ARRAY(dst.ue_cap, src->ue_cap);
|
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_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);
|
CP_OPT_BYTE_ARRAY(dst.ho_prep_info, src->ho_prep_info);
|
||||||
return dst;
|
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, cg_configinfo, eq_ba);
|
||||||
_F1_EQ_CHECK_OPTIONAL_IE(a, b, ue_cap, 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_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);
|
_F1_EQ_CHECK_OPTIONAL_IE(a, b, ho_prep_info, eq_ba);
|
||||||
return true;
|
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->cg_configinfo);
|
||||||
FREE_OPT_BYTE_ARRAY(cu2du->ue_cap);
|
FREE_OPT_BYTE_ARRAY(cu2du->ue_cap);
|
||||||
FREE_OPT_BYTE_ARRAY(cu2du->meas_config);
|
FREE_OPT_BYTE_ARRAY(cu2du->meas_config);
|
||||||
|
FREE_OPT_BYTE_ARRAY(cu2du->meas_timing_config);
|
||||||
FREE_OPT_BYTE_ARRAY(cu2du->ho_prep_info);
|
FREE_OPT_BYTE_ARRAY(cu2du->ho_prep_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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.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.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_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.cu_to_du_rrc_info.ho_prep_info = get_malloced_test_ba("PREPARATION DATA FOR HANDOVER");
|
||||||
orig.srbs_len = 2;
|
orig.srbs_len = 2;
|
||||||
orig.srbs = calloc_or_fail(orig.srbs_len, sizeof(*orig.srbs));
|
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"),
|
.cg_configinfo = get_malloced_test_ba("Modified"),
|
||||||
.ue_cap = get_malloced_test_ba("UE forgot to send earlier"),
|
.ue_cap = get_malloced_test_ba("UE forgot to send earlier"),
|
||||||
.meas_config = get_malloced_test_ba("with updates from another cell"),
|
.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!"),
|
.ho_prep_info = get_malloced_test_ba("prepare to ho soon!"),
|
||||||
};
|
};
|
||||||
_F1_MALLOC(orig.cu_to_du_rrc_info, cu2du);
|
_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);
|
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);
|
nr_mac_update_timers(module_idP, frame, slot);
|
||||||
|
|
||||||
if (wait_prach_completed || get_softmodem_params()->phy_test) {
|
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;
|
NR_sched_pdsch_t *sched_pdsch = &sched_ctrl->sched_pdsch;
|
||||||
/* get the PID of a HARQ process awaiting retrnasmission, or -1 otherwise */
|
/* get the PID of a HARQ process awaiting retrnasmission, or -1 otherwise */
|
||||||
sched_pdsch->dl_harq_pid = sched_ctrl->retrans_dl_harq.head;
|
sched_pdsch->dl_harq_pid = sched_ctrl->retrans_dl_harq.head;
|
||||||
|
|
||||||
/* Calculate Throughput */
|
/* Calculate Throughput */
|
||||||
const float a = 0.01f;
|
const float a = 0.01f;
|
||||||
const uint32_t b = UE->mac_stats.dl.current_bytes;
|
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)
|
void nr_mac_clean_cellgroup(NR_CellGroupConfig_t *cell_group)
|
||||||
{
|
{
|
||||||
DevAssert(cell_group != NULL);
|
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)) {
|
if (nr_timer_tick(&sched_ctrl->transm_interrupt)) {
|
||||||
/* expired */
|
/* expired */
|
||||||
nr_timer_stop(&sched_ctrl->transm_interrupt);
|
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);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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_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,
|
int nr_acknack_scheduling(gNB_MAC_INST *mac,
|
||||||
NR_UE_info_t *UE,
|
NR_UE_info_t *UE,
|
||||||
frame_t frameP,
|
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) {
|
} 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);
|
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
|
/* 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,
|
* 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;
|
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);
|
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);
|
NR_SCHED_UNLOCK(&mac->sched_lock);
|
||||||
|
|
||||||
mac->mac_rrc.ue_context_setup_response(&resp);
|
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;
|
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);
|
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);
|
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;
|
UE->CellGroup = oldUE->CellGroup;
|
||||||
oldUE->CellGroup = NULL;
|
oldUE->CellGroup = NULL;
|
||||||
UE->mac_stats = oldUE->mac_stats;
|
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
|
/* 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
|
* from the current configuration. It will be reapplied when the
|
||||||
* reconfiguration has succeeded (indicated by the CU) */
|
* 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));
|
du2cu->cellGroupConfig = malloc(du2cu->cellGroupConfig_length * sizeof(*du2cu->cellGroupConfig));
|
||||||
AssertFatal(du2cu->cellGroupConfig != NULL, "out of memory\n");
|
AssertFatal(du2cu->cellGroupConfig != NULL, "out of memory\n");
|
||||||
memcpy(du2cu->cellGroupConfig, required->du_to_cu_rrc_information->cellGroupConfig, du2cu->cellGroupConfig_length);
|
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");
|
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;
|
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));
|
du2cu->cellGroupConfig = malloc(du2cu->cellGroupConfig_length * sizeof(*du2cu->cellGroupConfig));
|
||||||
AssertFatal(du2cu->cellGroupConfig != NULL, "out of memory\n");
|
AssertFatal(du2cu->cellGroupConfig != NULL, "out of memory\n");
|
||||||
memcpy(du2cu->cellGroupConfig, required->du_to_cu_rrc_information->cellGroupConfig, du2cu->cellGroupConfig_length);
|
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");
|
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;
|
f1ap_msg->cause = required->cause;
|
||||||
|
|||||||
@@ -733,7 +733,20 @@ typedef struct nr_mac_rrc_ul_if_s {
|
|||||||
initial_ul_rrc_message_transfer_func_t initial_ul_rrc_message_transfer;
|
initial_ul_rrc_message_transfer_func_t initial_ul_rrc_message_transfer;
|
||||||
} nr_mac_rrc_ul_if_t;
|
} 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*/
|
/*! \brief UE list used by gNB to order UEs/CC for scheduling*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -753,6 +766,7 @@ typedef struct {
|
|||||||
NR_SpCellConfig_t *reconfigSpCellConfig;
|
NR_SpCellConfig_t *reconfigSpCellConfig;
|
||||||
interrupt_followup_action_t interrupt_action;
|
interrupt_followup_action_t interrupt_action;
|
||||||
NR_UE_NR_Capability_t *capability;
|
NR_UE_NR_Capability_t *capability;
|
||||||
|
measgap_config_t measgap_config;
|
||||||
// UE selected beam index
|
// UE selected beam index
|
||||||
uint8_t UE_beam_index;
|
uint8_t UE_beam_index;
|
||||||
float ul_thr_ue;
|
float ul_thr_ue;
|
||||||
|
|||||||
@@ -4090,3 +4090,198 @@ NR_ReconfigurationWithSync_t *get_reconfiguration_with_sync(rnti_t rnti, uid_t u
|
|||||||
|
|
||||||
return reconfigurationWithSync;
|
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_BCCH-DL-SCH-Message.h"
|
||||||
#include "NR_CellGroupConfig.h"
|
#include "NR_CellGroupConfig.h"
|
||||||
#include "NR_UE-NR-Capability.h"
|
#include "NR_UE-NR-Capability.h"
|
||||||
|
#include "NR_MeasConfig.h"
|
||||||
|
#include "NR_MeasurementTimingConfiguration.h"
|
||||||
#include "NR_UL-CCCH-Message.h"
|
#include "NR_UL-CCCH-Message.h"
|
||||||
#include "f1ap_messages_types.h"
|
#include "f1ap_messages_types.h"
|
||||||
#include "common/platform_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;
|
struct nr_mac_timers;
|
||||||
typedef struct nr_mac_timers nr_mac_timers_t;
|
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,
|
void nr_rrc_config_dl_tda(struct NR_PDSCH_TimeDomainResourceAllocationList *pdsch_TimeDomainAllocationList,
|
||||||
frame_type_t frame_type,
|
frame_type_t frame_type,
|
||||||
NR_TDD_UL_DL_ConfigCommon_t *tdd_UL_DL_ConfigurationCommon,
|
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,
|
long priority,
|
||||||
const nr_rlc_configuration_t *default_rlc_config);
|
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
|
#endif
|
||||||
|
|||||||
@@ -956,23 +956,59 @@ int do_RRCReestablishmentComplete(uint8_t *buffer, size_t buffer_size, int64_t r
|
|||||||
return((enc_rval.encoded+7)/8);
|
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,
|
NR_MeasConfig_t *get_MeasConfig(const NR_MeasTiming_t *mt,
|
||||||
int band,
|
int band,
|
||||||
int scs,
|
int scs,
|
||||||
NR_ReportConfigToAddMod_t *rc_PER,
|
NR_ReportConfigToAddMod_t *rc_PER,
|
||||||
NR_ReportConfigToAddMod_t *rc_A2,
|
NR_ReportConfigToAddMod_t *rc_A2,
|
||||||
seq_arr_t *rc_A3_seq,
|
seq_arr_t *rc_A3_seq,
|
||||||
seq_arr_t *pci_seq)
|
seq_arr_t *neigh_seq)
|
||||||
{
|
{
|
||||||
NR_MeasConfig_t *mc = calloc(1, sizeof(*mc));
|
NR_MeasConfig_t *mc = calloc_or_fail(1, sizeof(*mc));
|
||||||
mc->measObjectToAddModList = calloc(1, sizeof(*mc->measObjectToAddModList));
|
mc->measObjectToAddModList = calloc_or_fail(1, sizeof(*mc->measObjectToAddModList));
|
||||||
mc->reportConfigToAddModList = calloc(1, sizeof(*mc->reportConfigToAddModList));
|
mc->reportConfigToAddModList = calloc_or_fail(1, sizeof(*mc->reportConfigToAddModList));
|
||||||
mc->measIdToAddModList = calloc(1, sizeof(*mc->measIdToAddModList));
|
mc->measIdToAddModList = calloc_or_fail(1, sizeof(*mc->measIdToAddModList));
|
||||||
|
|
||||||
if (rc_PER)
|
if (rc_PER)
|
||||||
asn1cSeqAdd(&mc->reportConfigToAddModList->list, rc_PER);
|
asn1cSeqAdd(&mc->reportConfigToAddModList->list, rc_PER);
|
||||||
|
|
||||||
if (rc_A2)
|
if (rc_A2)
|
||||||
asn1cSeqAdd(&mc->reportConfigToAddModList->list, rc_A2);
|
asn1cSeqAdd(&mc->reportConfigToAddModList->list, rc_A2);
|
||||||
|
|
||||||
if (rc_A3_seq) {
|
if (rc_A3_seq) {
|
||||||
@@ -984,57 +1020,51 @@ NR_MeasConfig_t *get_MeasConfig(const NR_MeasTiming_t *mt,
|
|||||||
|
|
||||||
DevAssert(mt != NULL && mt->frequencyAndTiming != NULL);
|
DevAssert(mt != NULL && mt->frequencyAndTiming != NULL);
|
||||||
const struct NR_MeasTiming__frequencyAndTiming *ft = mt->frequencyAndTiming;
|
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
|
// 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.
|
// cell-specific offsets, blacklisted cells to be ignored and whitelisted cells to consider for measurements.
|
||||||
NR_MeasObjectToAddMod_t *mo1 = calloc(1, sizeof(*mo1));
|
NR_MeasObjectToAddMod_t *mo1 = get_MeasObject(ft, band, ft->carrierFreq, 1);
|
||||||
mo1->measObjectId = 1;
|
if (neigh_seq) {
|
||||||
mo1->measObject.present = NR_MeasObjectToAddMod__measObject_PR_measObjectNR;
|
NR_MeasObjectNR_t *monr1 = mo1->measObject.choice.measObjectNR;
|
||||||
NR_MeasObjectNR_t *monr1 = calloc(1, sizeof(*monr1));
|
if (monr1->cellsToAddModList == NULL)
|
||||||
asn1cCallocOne(monr1->ssbFrequency, ft->carrierFreq);
|
monr1->cellsToAddModList = calloc_or_fail(1, sizeof(*monr1->cellsToAddModList));
|
||||||
asn1cCallocOne(monr1->ssbSubcarrierSpacing, ft->ssbSubcarrierSpacing);
|
FOR_EACH_SEQ_ARR(nr_neighbour_cell_t *, n, neigh_seq) {
|
||||||
monr1->referenceSignalConfig.ssb_ConfigMobility = calloc(1, sizeof(*monr1->referenceSignalConfig.ssb_ConfigMobility));
|
NR_CellsToAddMod_t *cell = calloc_or_fail(1, sizeof(*cell));
|
||||||
monr1->referenceSignalConfig.ssb_ConfigMobility->deriveSSB_IndexFromCell = true;
|
cell->physCellId = n->physicalCellId;
|
||||||
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;
|
|
||||||
ASN_SEQUENCE_ADD(&monr1->cellsToAddModList->list, cell);
|
ASN_SEQUENCE_ADD(&monr1->cellsToAddModList->list, cell);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mo1->measObject.choice.measObjectNR = monr1;
|
|
||||||
asn1cSeqAdd(&mc->measObjectToAddModList->list, mo1);
|
asn1cSeqAdd(&mc->measObjectToAddModList->list, mo1);
|
||||||
|
|
||||||
// Preparation of measId
|
// 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;
|
const NR_ReportConfigId_t reportId = mc->reportConfigToAddModList->list.array[reportIdx]->reportConfigId;
|
||||||
NR_MeasIdToAddMod_t *measid = calloc(1, sizeof(NR_MeasIdToAddMod_t));
|
NR_MeasIdToAddMod_t *measid = get_MeasId(reportIdx + 1, reportId, 1);
|
||||||
measid->measId = reportIdx + 1;
|
|
||||||
measid->reportConfigId = reportId;
|
|
||||||
measid->measObjectId = 1;
|
|
||||||
asn1cSeqAdd(&mc->measIdToAddModList->list, measid);
|
asn1cSeqAdd(&mc->measIdToAddModList->list, measid);
|
||||||
}
|
}
|
||||||
|
|
||||||
mc->quantityConfig = calloc(1, sizeof(*mc->quantityConfig));
|
// Preparation of measId for neighbour cells for periodic report
|
||||||
mc->quantityConfig->quantityConfigNR_List = calloc(1, sizeof(*mc->quantityConfig->quantityConfigNR_List));
|
if (neigh_seq) {
|
||||||
NR_QuantityConfigNR_t *qcnr = calloc(1, sizeof(*qcnr));
|
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.ssb_FilterConfig.filterCoefficientRSRP, NR_FilterCoefficient_fc6);
|
||||||
asn1cCallocOne(qcnr->quantityConfigCell.csi_RS_FilterConfig.filterCoefficientRSRP, NR_FilterCoefficient_fc6);
|
asn1cCallocOne(qcnr->quantityConfigCell.csi_RS_FilterConfig.filterCoefficientRSRP, NR_FilterCoefficient_fc6);
|
||||||
asn1cSeqAdd(&mc->quantityConfig->quantityConfigNR_List->list, qcnr);
|
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);
|
ASN_STRUCT_FREE(asn_DEF_NR_HandoverPreparationInformation, hpi);
|
||||||
return (enc_rval.encoded + 7) / 8;
|
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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -50,6 +50,7 @@
|
|||||||
#include "NR_ReestablishmentCause.h"
|
#include "NR_ReestablishmentCause.h"
|
||||||
#include "NR_SRB-ToAddModList.h"
|
#include "NR_SRB-ToAddModList.h"
|
||||||
#include "NR_SecurityConfig.h"
|
#include "NR_SecurityConfig.h"
|
||||||
|
#include "NR_MeasurementTimingConfiguration.h"
|
||||||
#include "ds/seq_arr.h"
|
#include "ds/seq_arr.h"
|
||||||
#include "ds/byte_array.h"
|
#include "ds/byte_array.h"
|
||||||
#include "rrc_messages_types.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_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_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);
|
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_PER,
|
||||||
NR_ReportConfigToAddMod_t *rc_A2,
|
NR_ReportConfigToAddMod_t *rc_A2,
|
||||||
seq_arr_t *rc_A3_seq,
|
seq_arr_t *rc_A3_seq,
|
||||||
seq_arr_t *pci_seq);
|
seq_arr_t *neigh_seq);
|
||||||
void free_MeasConfig(NR_MeasConfig_t *mc);
|
void free_MeasConfig(NR_MeasConfig_t *mc);
|
||||||
int do_NR_Paging(uint8_t Mod_id, uint8_t *buffer, uint32_t tmsi);
|
int do_NR_Paging(uint8_t Mod_id, uint8_t *buffer, uint32_t tmsi);
|
||||||
|
|
||||||
|
|||||||
@@ -72,6 +72,8 @@ void rrc_gNB_generate_dedicatedRRCReconfiguration_release(gNB_RRC_INST *rrc,
|
|||||||
uint32_t nas_length,
|
uint32_t nas_length,
|
||||||
uint8_t *nas_buffer);
|
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);
|
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_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);
|
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
@@ -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]);
|
FREE_AND_ZERO_BYTE_ARRAY(params.dedicated_NAS_msg_list[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @brief Prepare the instance of RRCReconfigurationParams to be pass to RRC encoding */
|
NR_MeasConfig_t *nr_rrc_get_measconfig(const gNB_RRC_INST *rrc, uint64_t nr_cellid)
|
||||||
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);
|
nr_rrc_du_container_t *du = get_du_by_cell_id((gNB_RRC_INST *)rrc, nr_cellid);
|
||||||
nr_rrc_du_container_t *du = get_du_for_ue(rrc, UE->rrc_ue_id);
|
|
||||||
DevAssert(du != NULL);
|
DevAssert(du != NULL);
|
||||||
f1ap_served_cell_info_t *cell_info = &du->setup_req->cell[0].info;
|
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_PER = NULL;
|
||||||
NR_ReportConfigToAddMod_t *rc_A2 = NULL;
|
NR_ReportConfigToAddMod_t *rc_A2 = NULL;
|
||||||
seq_arr_t *rc_A3_seq = NULL;
|
seq_arr_t *rc_A3_seq = NULL;
|
||||||
seq_arr_t *pci_seq = NULL;
|
|
||||||
if (du->mtc != NULL) {
|
if (du->mtc != NULL) {
|
||||||
int scs = get_ssb_scs(cell_info);
|
int scs = get_ssb_scs(cell_info);
|
||||||
int band = get_dl_band(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)
|
if (neighbour_config)
|
||||||
neighbour_cells = neighbour_config->neighbour_cells;
|
neighbour_cells = neighbour_config->neighbour_cells;
|
||||||
|
|
||||||
if (neighbour_cells
|
if (neighbour_cells && rrc->measurementConfiguration.a3_event_list && rrc->measurementConfiguration.a3_event_list->size > 0) {
|
||||||
&& rrc->measurementConfiguration.a3_event_list
|
|
||||||
&& rrc->measurementConfiguration.a3_event_list->size > 0) {
|
|
||||||
/* Loop through neighbours and find related A3 configuration
|
/* Loop through neighbours and find related A3 configuration
|
||||||
If no related A3 but there is default add the default one.
|
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.
|
If default one added once as a report, no need to add it again && duplication.
|
||||||
*/
|
*/
|
||||||
rc_A3_seq = malloc(sizeof(seq_arr_t));
|
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(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");
|
LOG_D(NR_RRC, "HO LOG: Preparing A3 Event Measurement Configuration!\n");
|
||||||
bool is_default_a3_added = false;
|
bool is_default_a3_added = false;
|
||||||
for (int i = 0; i < neighbour_cells->size; i++) {
|
for (int i = 0; i < neighbour_cells->size; i++) {
|
||||||
nr_neighbour_cell_t *neighbourCell = (nr_neighbour_cell_t *)seq_arr_at(neighbour_cells, i);
|
nr_neighbour_cell_t *neighbourCell = (nr_neighbour_cell_t *)seq_arr_at(neighbour_cells, i);
|
||||||
if (!neighbourCell->isIntraFrequencyNeighbour)
|
const nr_a3_event_t *a3Event = get_a3_configuration((gNB_RRC_INST *)rrc, neighbourCell->physicalCellId);
|
||||||
continue;
|
|
||||||
seq_arr_push_back(pci_seq, &neighbourCell->physicalCellId, sizeof(int));
|
|
||||||
const nr_a3_event_t *a3Event = get_a3_configuration(rrc, neighbourCell->physicalCellId);
|
|
||||||
if (!a3Event || is_default_a3_added)
|
if (!a3Event || is_default_a3_added)
|
||||||
continue;
|
continue;
|
||||||
if (a3Event->pci == -1)
|
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);
|
rc_PER = prepare_periodic_event_report(rrc->measurementConfiguration.per_event);
|
||||||
if (rrc->measurementConfiguration.a2_event)
|
if (rrc->measurementConfiguration.a2_event)
|
||||||
rc_A2 = prepare_a2_event_report(rrc->measurementConfiguration.a2_event);
|
rc_A2 = prepare_a2_event_report(rrc->measurementConfiguration.a2_event);
|
||||||
|
return get_MeasConfig(mt, band, scs, rc_PER, rc_A2, rc_A3_seq, neighbour_cells);
|
||||||
measconfig = get_MeasConfig(mt, band, scs, rc_PER, rc_A2, rc_A3_seq, pci_seq);
|
|
||||||
}
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
if (UE->measConfig)
|
/** @brief Prepare the instance of RRCReconfigurationParams to be pass to RRC encoding */
|
||||||
free_MeasConfig(UE->measConfig);
|
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)
|
||||||
|
{
|
||||||
UE->measConfig = measconfig;
|
uint8_t xid = rrc_gNB_get_next_transaction_identifier(rrc->module_id);
|
||||||
|
|
||||||
// Re-establish PDCP for SRB2 only
|
// Re-establish PDCP for SRB2 only
|
||||||
NR_SRB_ToAddModList_t *SRBs = createSRBlist(UE, srb_reest_bitmap);
|
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);
|
uint8_t new_xid = rrc_gNB_get_next_transaction_identifier(rrc->module_id);
|
||||||
ue_p->xids[new_xid] = RRC_REESTABLISH_COMPLETE;
|
ue_p->xids[new_xid] = RRC_REESTABLISH_COMPLETE;
|
||||||
nr_rrc_reconfig_param_t params = {.cell_group_config = cellGroupConfig,
|
nr_rrc_reconfig_param_t params = {.cell_group_config = cellGroupConfig,
|
||||||
|
.meas_config = ue_p->measConfig,
|
||||||
.drb_config_list = DRBs,
|
.drb_config_list = DRBs,
|
||||||
.srb_config_list = SRBs,
|
.srb_config_list = SRBs,
|
||||||
.transaction_id = new_xid};
|
.transaction_id = new_xid};
|
||||||
@@ -1189,6 +1180,7 @@ static void rrc_handle_RRCSetupRequest(gNB_RRC_INST *rrc,
|
|||||||
UE->nr_cellid = msg->nr_cellid;
|
UE->nr_cellid = msg->nr_cellid;
|
||||||
UE->masterCellGroup = cellGroupConfig;
|
UE->masterCellGroup = cellGroupConfig;
|
||||||
UE->ongoing_reconfiguration = false;
|
UE->ongoing_reconfiguration = false;
|
||||||
|
UE->measConfig = nr_rrc_get_measconfig(rrc, UE->nr_cellid);
|
||||||
activate_srb(UE, 1);
|
activate_srb(UE, 1);
|
||||||
rrc_gNB_generate_RRCSetup(0, msg->crnti, ue_context_p, msg->du2cu_rrc_container, msg->du2cu_rrc_container_length);
|
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);
|
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");
|
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) {
|
if (UE->masterCellGroup) {
|
||||||
ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, 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);
|
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);
|
*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;
|
int nb_srb = 1;
|
||||||
f1ap_srb_to_setup_t *srbs = calloc_or_fail(nb_srb, sizeof(*srbs));
|
f1ap_srb_to_setup_t *srbs = calloc_or_fail(nb_srb, sizeof(*srbs));
|
||||||
srbs[0].id = 2;
|
srbs[0].id = 2;
|
||||||
|
|||||||
@@ -181,6 +181,19 @@ static void nr_initiate_handover(const gNB_RRC_INST *rrc,
|
|||||||
srbs[0].id = 1;
|
srbs[0].id = 1;
|
||||||
srbs[1].id = 2;
|
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));
|
byte_array_t *hpi = malloc_or_fail(sizeof(*hpi));
|
||||||
*hpi = copy_byte_array(*ho_prep_info);
|
*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_len = nb_drb,
|
||||||
.drbs = drbs,
|
.drbs = drbs,
|
||||||
.cu_to_du_rrc_info.ho_prep_info = hpi,
|
.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,
|
.gnb_du_ue_agg_mbr_ul = ue_agg_mbr,
|
||||||
};
|
};
|
||||||
rrc->mac_rrc.ue_context_setup_request(target_du->assoc_id, &ue_context_setup_req);
|
rrc->mac_rrc.ue_context_setup_request(target_du->assoc_id, &ue_context_setup_req);
|
||||||
|
|||||||
@@ -1871,8 +1871,7 @@ static void nr_rrc_ue_process_measConfig(rrcPerNB_t *rrc, NR_MeasConfig_t *const
|
|||||||
if (measConfig->measIdToAddModList)
|
if (measConfig->measIdToAddModList)
|
||||||
handle_measid_addmod(rrc, measConfig->measIdToAddModList, timers);
|
handle_measid_addmod(rrc, measConfig->measIdToAddModList, timers);
|
||||||
|
|
||||||
AssertFatal(!measConfig->measGapConfig, "Measurement gaps not yet supported\n");
|
LOG_W(NR_RRC, "Measurement gaps not yet supported!\n");
|
||||||
AssertFatal(!measConfig->measGapSharingConfig, "Measurement gaps not yet supported\n");
|
|
||||||
|
|
||||||
if (measConfig->s_MeasureConfig) {
|
if (measConfig->s_MeasureConfig) {
|
||||||
if (measConfig->s_MeasureConfig->present == NR_MeasConfig__s_MeasureConfig_PR_ssb_RSRP) {
|
if (measConfig->s_MeasureConfig->present == NR_MeasConfig__s_MeasureConfig_PR_ssb_RSRP) {
|
||||||
|
|||||||
Reference in New Issue
Block a user