Merge remote-tracking branch 'origin/gnb-du-config-cell-status' into integration_2025_w18 (!3416)

Signal gNB-DU config update CellStatus from DU to CU

Add the cell status to gNB-DU configuration update message, including
test. Use the gNB-DU configuration update to signal the cell status in
the stack.

This IE was not included in a previous refactoring round, but is at
least necessary to be decoded when interoperating with the OSC O-DU.
This commit is contained in:
Robert Schmidt
2025-05-05 22:37:21 +02:00
6 changed files with 115 additions and 23 deletions
+11
View File
@@ -262,6 +262,13 @@ typedef struct f1ap_gnb_cu_configuration_update_failure_s {
uint16_t criticality_diagnostics;
} f1ap_gnb_cu_configuration_update_failure_t;
typedef struct f1ap_cell_status_t {
// NR CGI
plmn_id_t plmn;
uint64_t nr_cellid; // NR Global Cell Id
enum { F1AP_STATE_IN_SERVICE, F1AP_STATE_OUT_OF_SERVICE} service_state;
} f1ap_cell_status_t;
/*DU configuration messages*/
typedef struct f1ap_gnb_du_configuration_update_s {
/*TODO UPDATE TO SUPPORT DU CONFIG*/
@@ -291,6 +298,10 @@ typedef struct f1ap_gnb_du_configuration_update_s {
plmn_id_t plmn;
uint64_t nr_cellid; // NR Global Cell Id
} cell_to_delete[F1AP_MAX_NB_CELLS];
f1ap_cell_status_t status[F1AP_MAX_NB_CELLS];
int num_status;
/// gNB-DU unique ID, at least within a gNB-CU (0 .. 2^36 - 1)
uint64_t *gNB_DU_ID;
} f1ap_gnb_du_configuration_update_t;
+51 -1
View File
@@ -1455,6 +1455,28 @@ F1AP_F1AP_PDU_t *encode_f1ap_du_configuration_update(const f1ap_gnb_du_configura
}
}
if (msg->num_status > 0) {
asn1cSequenceAdd(out->protocolIEs.list, F1AP_GNBDUConfigurationUpdateIEs_t, ie4);
ie4->id = F1AP_ProtocolIE_ID_id_Cells_Status_List;
ie4->criticality = F1AP_Criticality_reject;
ie4->value.present = F1AP_GNBDUConfigurationUpdateIEs__value_PR_Cells_Status_List;
for (int i = 0; i < msg->num_status; i++) {
const f1ap_cell_status_t *cs = &msg->status[i];
asn1cSequenceAdd(ie4->value.choice.Cells_Status_List.list, F1AP_Cells_Status_ItemIEs_t, cell_status);
cell_status->id = F1AP_ProtocolIE_ID_id_Cells_Status_Item;
cell_status->criticality = F1AP_Criticality_reject;
cell_status->value.present = F1AP_Cells_Status_ItemIEs__value_PR_Cells_Status_Item;
F1AP_Cells_Status_Item_t *cell_status_item = &cell_status->value.choice.Cells_Status_Item;
F1AP_NRCGI_t *nrcgi = &cell_status_item->nRCGI;
MCC_MNC_TO_PLMNID(cs->plmn.mcc, cs->plmn.mnc, cs->plmn.mnc_digit_length, &nrcgi->pLMN_Identity);
NR_CELL_ID_TO_BIT_STRING(cs->nr_cellid, &nrcgi->nRCellIdentity);
F1AP_Service_Status_t *ss = &cell_status_item->service_status;
ss->service_state =
cs->service_state == F1AP_STATE_IN_SERVICE ? F1AP_Service_State_in_service : F1AP_Service_State_out_of_service;
}
}
/* optional */
/* c5. GNB_DU_ID (integer value) */
if (msg->gNB_DU_ID != NULL) {
@@ -1551,7 +1573,21 @@ bool decode_f1ap_du_configuration_update(const F1AP_F1AP_PDU_t *pdu, f1ap_gnb_du
} break;
case F1AP_ProtocolIE_ID_id_Cells_Status_List:
/* Cells Status List (O) */
AssertError(1 == 0, return false, "F1AP_ProtocolIE_ID_id_Cells_Status_List is not supported");
out->num_status = ie->value.choice.Cells_Status_List.list.count;
for (int i = 0; i < out->num_status; ++i) {
const F1AP_Cells_Status_ItemIEs_t *csi_ie =
(F1AP_Cells_Status_ItemIEs_t *)ie->value.choice.Cells_Status_List.list.array[i];
AssertError(csi_ie->value.present == F1AP_Cells_Status_ItemIEs__value_PR_Cells_Status_Item,
return false,
"CellStatus_ItemIE has no cell status\n");
const F1AP_Cells_Status_Item_t *f1ap_cell_status = &csi_ie->value.choice.Cells_Status_Item;
const F1AP_NRCGI_t *nrcgi = &f1ap_cell_status->nRCGI;
f1ap_cell_status_t *cs = &out->status[i];
PLMNID_TO_MCC_MNC(&nrcgi->pLMN_Identity, cs->plmn.mcc, cs->plmn.mnc, cs->plmn.mnc_digit_length);
BIT_STRING_TO_NR_CELL_IDENTITY(&nrcgi->nRCellIdentity, cs->nr_cellid);
F1AP_Service_State_t state = f1ap_cell_status->service_status.service_state;
cs->service_state = state == F1AP_Service_State_in_service ? F1AP_STATE_IN_SERVICE : F1AP_STATE_OUT_OF_SERVICE;
}
break;
case F1AP_ProtocolIE_ID_id_Dedicated_SIDelivery_NeededUE_List:
/* Dedicated SI Delivery Needed UE List (O) */
@@ -1618,6 +1654,16 @@ bool eq_f1ap_du_configuration_update(const f1ap_gnb_du_configuration_update_t *a
if (!eq_f1ap_sys_info(a->cell_to_modify[i].sys_info, b->cell_to_modify[i].sys_info))
return false;
}
/* cell status */
_F1_EQ_CHECK_INT(a->num_status, b->num_status);
for (int i = 0; i < a->num_status; ++i) {
const f1ap_cell_status_t *astatus = &a->status[i];
const f1ap_cell_status_t *bstatus = &b->status[i];
if (!eq_f1ap_plmn(&astatus->plmn, &bstatus->plmn))
return false;
_F1_EQ_CHECK_LONG(astatus->nr_cellid, bstatus->nr_cellid);
_F1_EQ_CHECK_INT(astatus->service_state, bstatus->service_state);
}
return true;
}
@@ -1654,6 +1700,10 @@ f1ap_gnb_du_configuration_update_t cp_f1ap_du_configuration_update(const f1ap_gn
cp.cell_to_modify[i].info = copy_f1ap_served_cell_info(&msg->cell_to_modify[i].info);
cp.cell_to_modify[i].sys_info = copy_f1ap_gnb_du_system_info(msg->cell_to_modify[i].sys_info);
}
/* cell status */
cp.num_status = msg->num_status;
for (int i = 0; i < cp.num_status; ++i)
cp.status[i] = msg->status[i];
return cp;
}
+11
View File
@@ -564,6 +564,17 @@ static void test_f1ap_du_configuration_update(void)
.cell_to_delete[0].plmn.mcc = 1,
.cell_to_delete[0].plmn.mnc = 1,
.cell_to_delete[0].plmn.mnc_digit_length = 3,
.num_status = 2,
.status[0].nr_cellid = 542UL,
.status[0].plmn.mcc = 1,
.status[0].plmn.mnc = 2,
.status[0].plmn.mnc_digit_length = 3,
.status[0].service_state = F1AP_STATE_IN_SERVICE,
.status[1].nr_cellid = 33UL,
.status[1].plmn.mcc = 5,
.status[1].plmn.mnc = 13,
.status[1].plmn.mnc_digit_length = 2,
.status[1].service_state = F1AP_STATE_OUT_OF_SERVICE,
};
orig.cell_to_modify[0].sys_info = calloc_or_fail(1, sizeof(*orig.cell_to_modify[0].sys_info));
*orig.cell_to_modify[0].sys_info = sys_info;
+10 -4
View File
@@ -942,11 +942,17 @@ void prepare_du_configuration_update(gNB_MAC_INST *mac,
/* send gNB-DU configuration update to RRC */
f1ap_gnb_du_configuration_update_t update = {
.transaction_id = 1,
.num_cells_to_modify = 1,
.num_status = 1,
.status[0].plmn = info->plmn,
.status[0].nr_cellid = info->nr_cellid,
.status[0].service_state = F1AP_STATE_IN_SERVICE,
};
update.cell_to_modify[0].old_nr_cellid = info->nr_cellid;
update.cell_to_modify[0].info = *info;
update.cell_to_modify[0].sys_info = get_sys_info(mib, sib1, NULL);
if (mib && sib1) {
update.num_cells_to_modify = 1,
update.cell_to_modify[0].old_nr_cellid = info->nr_cellid;
update.cell_to_modify[0].info = *info;
update.cell_to_modify[0].sys_info = get_sys_info(mib, sib1, NULL);
}
mac->mac_rrc.gnb_du_configuration_update(&update);
}
@@ -168,18 +168,25 @@ void f1_setup_response(const f1ap_setup_resp_t *resp)
// we can configure other SIB only after having received the ones generated by CU
bool update = nr_mac_configure_other_sib(mac, cu_cell->num_SI, cu_cell->SI_msg);
/* only if we have to update SIB1, set mib and sib1 to non-NULL to indicate
* in du_config_update that it has changed (otherwise, gNB-DU config update
* indicates only cell status */
NR_BCCH_BCH_Message_t *mib = NULL;
NR_BCCH_DL_SCH_Message_t *sib1 = NULL;
if (update) {
f1ap_setup_req_t *sr = mac->f1_config.setup_req;
DevAssert(sr->num_cells_available == 1);
f1ap_served_cell_info_t *info = &sr->cell[0].info;
NR_COMMON_channels_t *cc = &mac->common_channels[0];
prepare_du_configuration_update(mac, info, cc->mib, cc->sib1);
mib = cc->mib;
sib1 = cc->sib1;
}
mac->f1_config.setup_resp = malloc(sizeof(*mac->f1_config.setup_resp));
AssertFatal(mac->f1_config.setup_resp != NULL, "out of memory\n");
// Copy F1AP message
*mac->f1_config.setup_resp = cp_f1ap_setup_response(resp);
f1ap_setup_req_t *sr = mac->f1_config.setup_req;
DevAssert(sr->num_cells_available == 1);
prepare_du_configuration_update(mac, &sr->cell[0].info, mib, sib1);
NR_SCHED_UNLOCK(&mac->sched_lock);
// NOTE: Before accepting any UEs, we should initialize the UE states.
+21 -14
View File
@@ -111,7 +111,7 @@ static bool extract_sys_info(const f1ap_gnb_du_system_info_t *sys_info, NR_MIB_t
asn_dec_rval_t dec_rval = uper_decode_complete(NULL, &asn_DEF_NR_MIB, (void **)mib, sys_info->mib, sys_info->mib_length);
if (dec_rval.code != RC_OK) {
LOG_E(RRC, "Failed to decode NR_MIB (%zu bits) of DU\n", dec_rval.consumed);
LOG_E(NR_RRC, "Failed to decode NR_MIB (%zu bits) of DU\n", dec_rval.consumed);
ASN_STRUCT_FREE(asn_DEF_NR_MIB, *mib);
return false;
}
@@ -121,7 +121,7 @@ static bool extract_sys_info(const f1ap_gnb_du_system_info_t *sys_info, NR_MIB_t
if (dec_rval.code != RC_OK) {
ASN_STRUCT_FREE(asn_DEF_NR_MIB, *mib);
ASN_STRUCT_FREE(asn_DEF_NR_SIB1, *sib1);
LOG_E(RRC, "Failed to decode NR_SIB1 (%zu bits), rejecting DU\n", dec_rval.consumed);
LOG_E(NR_RRC, "Failed to decode NR_SIB1 (%zu bits), rejecting DU\n", dec_rval.consumed);
return false;
}
}
@@ -149,7 +149,7 @@ static NR_MeasurementTimingConfiguration_t *extract_mtc(uint8_t *buf, int buf_le
|| mtc->criticalExtensions.choice.c1->choice.measTimingConf == NULL
|| mtc->criticalExtensions.choice.c1->choice.measTimingConf->measTiming == NULL
|| mtc->criticalExtensions.choice.c1->choice.measTimingConf->measTiming->list.count == 0) {
LOG_E(RRC, "error: measurementTimingConfiguration does not have expected format (at least one measTiming entry\n");
LOG_E(NR_RRC, "error: measurementTimingConfiguration does not have expected format (at least one measTiming entry\n");
if (LOG_DEBUGFLAG(DEBUG_ASN1))
xer_fprint(stdout, &asn_DEF_NR_MeasurementTimingConfiguration, mtc);
ASN_STRUCT_FREE(asn_DEF_NR_MeasurementTimingConfiguration, mtc);
@@ -370,15 +370,15 @@ void rrc_gNB_process_f1_setup_req(f1ap_setup_req_t *req, sctp_assoc_t assoc_id)
if (sys_info != NULL && sys_info->mib != NULL && !(sys_info->sib1 == NULL && IS_SA_MODE(get_softmodem_params()))) {
if (!extract_sys_info(sys_info, &mib, &sib1)) {
LOG_W(RRC, "rejecting DU ID %ld\n", req->gNB_DU_id);
LOG_W(NR_RRC, "rejecting DU ID %ld\n", req->gNB_DU_id);
fail.cause = F1AP_CauseProtocol_semantic_error;
rrc->mac_rrc.f1_setup_failure(assoc_id, &fail);
ASN_STRUCT_FREE(asn_DEF_NR_MeasurementTimingConfiguration, mtc);
return;
}
}
LOG_I(RRC, "Accepting DU %ld (%s), sending F1 Setup Response\n", req->gNB_DU_id, req->gNB_DU_name);
LOG_I(RRC, "DU uses RRC version %u.%u.%u\n", req->rrc_ver[0], req->rrc_ver[1], req->rrc_ver[2]);
LOG_I(NR_RRC, "Accepting DU %ld (%s), sending F1 Setup Response\n", req->gNB_DU_id, req->gNB_DU_name);
LOG_I(NR_RRC, "DU uses RRC version %u.%u.%u\n", req->rrc_ver[0], req->rrc_ver[1], req->rrc_ver[2]);
// we accept the DU
nr_rrc_du_container_t *du = calloc(1, sizeof(*du));
@@ -497,7 +497,7 @@ static void update_cell_info(nr_rrc_du_container_t *du, const f1ap_served_cell_i
ASN_STRUCT_FREE(asn_DEF_NR_MeasurementTimingConfiguration, du->mtc);
du->mtc = new_mtc;
} else {
LOG_E(RRC, "error decoding MeasurementTimingConfiguration during cell update, ignoring new config\n");
LOG_E(NR_RRC, "error decoding MeasurementTimingConfiguration during cell update, ignoring new config\n");
ASN_STRUCT_FREE(asn_DEF_NR_MeasurementTimingConfiguration, new_mtc);
}
}
@@ -520,7 +520,7 @@ void rrc_gNB_process_f1_du_configuration_update(f1ap_gnb_du_configuration_update
const f1ap_served_cell_info_t *info = &du->setup_req->cell[0].info;
if (conf_up->num_cells_to_add > 0) {
// Here we check if the number of cell limit is respectet, otherwise send failure
LOG_W(RRC, "du_configuration_update->cells_to_add_list is not supported yet");
LOG_W(NR_RRC, "du_configuration_update->cells_to_add_list is not supported yet");
}
if (conf_up->num_cells_to_modify > 0) {
@@ -528,13 +528,13 @@ void rrc_gNB_process_f1_du_configuration_update(f1ap_gnb_du_configuration_update
AssertFatal(conf_up->num_cells_to_modify == 1, "cannot handle more than one cell!\n");
if (info->nr_cellid != conf_up->cell_to_modify[0].old_nr_cellid) {
LOG_W(RRC, "no cell with ID %ld found, ignoring gNB-DU configuration update\n", conf_up->cell_to_modify[0].old_nr_cellid);
LOG_W(NR_RRC, "no cell with ID %ld found, ignoring gNB-DU configuration update\n", conf_up->cell_to_modify[0].old_nr_cellid);
return;
}
// verify the new plmn of the cell
if (!rrc_gNB_plmn_matches(rrc, &conf_up->cell_to_modify[0].info)) {
LOG_W(RRC, "PLMN does not match, ignoring gNB-DU configuration update\n");
LOG_W(NR_RRC, "PLMN does not match, ignoring gNB-DU configuration update\n");
return;
}
@@ -553,11 +553,11 @@ void rrc_gNB_process_f1_du_configuration_update(f1ap_gnb_du_configuration_update
NR_MIB_t *mib = NULL;
if (!extract_sys_info(sys_info, &mib, &du->sib1)) {
LOG_W(RRC, "cannot update sys_info for DU %ld\n", du->setup_req->gNB_DU_id);
LOG_W(NR_RRC, "cannot update sys_info for DU %ld\n", du->setup_req->gNB_DU_id);
} else {
DevAssert(mib != NULL);
du->mib = mib;
LOG_I(RRC, "update system information of DU %ld\n", du->setup_req->gNB_DU_id);
LOG_I(NR_RRC, "update system information of DU %ld\n", du->setup_req->gNB_DU_id);
}
}
if (du->mib != NULL && du->sib1 != NULL) {
@@ -568,7 +568,14 @@ void rrc_gNB_process_f1_du_configuration_update(f1ap_gnb_du_configuration_update
if (conf_up->num_cells_to_delete > 0) {
// delete the cell and send cell to desactive IE in the response.
LOG_W(RRC, "du_configuration_update->cells_to_delete_list is not supported yet");
LOG_W(NR_RRC, "du_configuration_update->cells_to_delete_list is not supported yet");
}
for (int i = 0; i < conf_up->num_status; ++i) {
const f1ap_cell_status_t *cs = &conf_up->status[i];
const char *status = cs->service_state == F1AP_STATE_IN_SERVICE ? "in service" : "out of service";
const plmn_id_t *p = &cs->plmn;
LOG_I(NR_RRC, "cell PLMN %03d.%0*d Cell ID %ld is %s\n", p->mcc, p->mnc_digit_length, p->mnc, cs->nr_cellid, status);
}
/* Send DU Configuration Acknowledgement */
@@ -589,7 +596,7 @@ void rrc_CU_process_f1_lost_connection(gNB_RRC_INST *rrc, f1ap_lost_connection_t
}
f1ap_setup_req_t *req = du->setup_req;
LOG_I(RRC, "releasing DU ID %ld (%s) on assoc_id %d\n", req->gNB_DU_id, req->gNB_DU_name, assoc_id);
LOG_I(NR_RRC, "releasing DU ID %ld (%s) on assoc_id %d\n", req->gNB_DU_id, req->gNB_DU_name, assoc_id);
ASN_STRUCT_FREE(asn_DEF_NR_MIB, du->mib);
ASN_STRUCT_FREE(asn_DEF_NR_SIB1, du->sib1);
ASN_STRUCT_FREE(asn_DEF_NR_MeasurementTimingConfiguration, du->mtc);