RRC/MAC: add SIB4 inter-frequency neighbour support
Add SIB4 encode/decode helpers and populate SIB4 from configured inter-frequency carriers and neighbour cells, wiring it into DU F1 Setup handling and MAC SystemInformation. Changes: - Add do_SIB4_NR() and NR_SIB4.h inclusion in asn1_msg.c/asn1_msg.h to UPER-encode NR_SIB4_t into a byte_array_t. - Extend nr_mac_configure_other_sib() in NR_MAC_gNB/config.c with an NR_SIB_4 case that validates the SIB container, decodes NR_SIB4_t via uper_decode, logs failures, and appends SIB4 to SystemInformation. - Introduce build_inter_freq_carrier_from_cfg() and get_sib4_inter_freq_neighbors() in rrc_gNB_du.c to build NR_InterFreqCarrierFreqInfo_t and NR_SIB4_t from inter-frequency and neighbour cell configuration, skipping invalid or empty carriers. - Handle NR_SIB_4 in rrc_gNB_process_f1_setup_req() by deriving SIB4 from neighbour/inter-frequency configuration, encoding it with do_SIB4_NR(), validating the resulting byte_array_t and adding the SI message for the DU cell when encoding succeeds.
This commit is contained in:
@@ -1023,6 +1023,21 @@ bool nr_mac_configure_other_sib(gNB_MAC_INST *nrmac, int num_cu_sib, const f1ap_
|
||||
add_sib_to_systeminformation(sysInfo, type);
|
||||
break;
|
||||
}
|
||||
case NR_SIB_4: {
|
||||
struct NR_SystemInformation_IEs__sib_TypeAndInfo__Member *type = calloc_or_fail(1, sizeof(*type));
|
||||
type->present = NR_SystemInformation_IEs__sib_TypeAndInfo__Member_PR_sib4;
|
||||
NR_SIB4_t *sib4 = NULL;
|
||||
asn_dec_rval_t dec_rval = uper_decode(NULL, &asn_DEF_NR_SIB4, (void **)&sib4, container->buf, container->len, 0, 0);
|
||||
if (dec_rval.code != RC_OK) {
|
||||
LOG_E(NR_MAC, "cannot decode SIB%d from CU\n", config_sibs[i]);
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_SIB4, sib4);
|
||||
free(type);
|
||||
break;
|
||||
}
|
||||
type->choice.sib4 = sib4;
|
||||
add_sib_to_systeminformation(sysInfo, type);
|
||||
break;
|
||||
}
|
||||
default :
|
||||
AssertFatal(false, "Invalid or not supported SIB%d\n", config_sibs[i]);
|
||||
}
|
||||
|
||||
@@ -265,6 +265,27 @@ byte_array_t do_SIB3_NR(const NR_SIB3_t *sib3)
|
||||
return msg;
|
||||
}
|
||||
|
||||
byte_array_t do_SIB4_NR(NR_SIB4_t *sib4)
|
||||
{
|
||||
byte_array_t msg = {.buf = NULL, .len = 0};
|
||||
char errbuf[256] = {0};
|
||||
size_t errlen = sizeof(errbuf);
|
||||
int ret = asn_check_constraints(&asn_DEF_NR_SIB4, sib4, errbuf, &errlen);
|
||||
if (ret != 0) {
|
||||
LOG_E(NR_RRC, "SIB4 constraint check failed: %s\n", errbuf);
|
||||
return msg;
|
||||
}
|
||||
|
||||
int val = uper_encode_to_new_buffer(&asn_DEF_NR_SIB4, NULL, (void *)sib4, (void **)&msg.buf);
|
||||
if (val <= 0) {
|
||||
LOG_E(NR_RRC, "Failed to encode SIB4\n");
|
||||
return msg;
|
||||
}
|
||||
|
||||
msg.len = val;
|
||||
return msg;
|
||||
}
|
||||
|
||||
int do_RRCReject(uint8_t *const buffer)
|
||||
{
|
||||
asn_enc_rval_t enc_rval;
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#include "NR_UE-CapabilityRAT-ContainerList.h"
|
||||
#include "NR_SIB2.h"
|
||||
#include "NR_SIB3.h"
|
||||
#include "NR_SIB4.h"
|
||||
#include "ds/seq_arr.h"
|
||||
#include "ds/byte_array.h"
|
||||
#include "openair2/LAYER2/nr_pdcp/nr_pdcp_configuration.h"
|
||||
@@ -69,6 +70,7 @@ int xer_sprint_NR(char *string, size_t string_size, struct asn_TYPE_descriptor_s
|
||||
|
||||
byte_array_t do_SIB2_NR(const NR_SIB2_t *sib2);
|
||||
byte_array_t do_SIB3_NR(const NR_SIB3_t *sib3);
|
||||
byte_array_t do_SIB4_NR(NR_SIB4_t *sib4);
|
||||
|
||||
int do_RRCReject(uint8_t *const buffer);
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "nr_rrc_defs.h"
|
||||
#include "NR_SIB2.h"
|
||||
#include "NR_SIB3.h"
|
||||
#include "NR_SIB4.h"
|
||||
#include "NR_Q-OffsetRange.h"
|
||||
#include "openair2/RRC/NR/MESSAGES/asn1_msg.h"
|
||||
#include "rrc_gNB_mobility.h"
|
||||
@@ -305,6 +306,130 @@ static NR_SIB3_t *get_sib3_intra_freq_neighbors(const seq_arr_t *neighbours, uin
|
||||
return sib3;
|
||||
}
|
||||
|
||||
/** @brief Build an NR_InterFreqCarrierFreqInfo structure from a nr_inter_freq_cfg_t configuration.
|
||||
* @param cfg Configuration to build the carrier from
|
||||
* @return Allocated NR_InterFreqCarrierFreqInfo_t on success, NULL if the configuration is invalid */
|
||||
static NR_InterFreqCarrierFreqInfo_t *build_inter_freq_carrier_from_cfg(const nr_inter_freq_cfg_t *cfg)
|
||||
{
|
||||
DevAssert(cfg);
|
||||
|
||||
const nr_neighbour_cell_sib4_freq_t *f = &cfg->freq_cfg;
|
||||
|
||||
NR_InterFreqCarrierFreqInfo_t *carrier = calloc_or_fail(1, sizeof(*carrier));
|
||||
/* dl_CarrierFreq (ARFCN) */
|
||||
carrier->dl_CarrierFreq = cfg->arfcn;
|
||||
/* ssbSubcarrierSpacing: (15/30/60/120 kHz) */
|
||||
carrier->ssbSubcarrierSpacing = cfg->scs;
|
||||
carrier->deriveSSB_IndexFromCell = true;
|
||||
/* Q-RxLevMin (-70..-22 dBm) */
|
||||
carrier->q_RxLevMin = cfg->q_RxLevMin;
|
||||
/* T-Reselection (0..7) */
|
||||
carrier->t_ReselectionNR = cfg->t_ReselectionNR;
|
||||
/* threshX_HighP/LowP: Reselection threshold (0..31) */
|
||||
carrier->threshX_HighP = f->threshX_HighP;
|
||||
carrier->threshX_LowP = f->threshX_LowP;
|
||||
/* threshX-Q: Optional RSRQ thresholds; emit only when both are provided. */
|
||||
if (f->threshX_HighQ != -1 && f->threshX_LowQ != -1) {
|
||||
carrier->threshX_Q = calloc_or_fail(1, sizeof(*carrier->threshX_Q));
|
||||
carrier->threshX_Q->threshX_HighQ = f->threshX_HighQ;
|
||||
carrier->threshX_Q->threshX_LowQ = f->threshX_LowQ;
|
||||
} else if (f->threshX_HighQ != -1 || f->threshX_LowQ != -1) {
|
||||
LOG_W(NR_RRC,
|
||||
"SIB4: partial threshX-Q for ARFCN %d (HighQ=%d LowQ=%d), omitting threshX-Q\n",
|
||||
cfg->arfcn,
|
||||
f->threshX_HighQ,
|
||||
f->threshX_LowQ);
|
||||
}
|
||||
/* CellReselectionPriority (0..7) */
|
||||
if (f->cellReselectionPriority != -1)
|
||||
asn1cCallocOne(carrier->cellReselectionPriority, f->cellReselectionPriority);
|
||||
/* q-OffsetFreq (-24..24 dB), 0 dB default */
|
||||
asn1cCallocOne(carrier->q_OffsetFreq, get_q_offset_asn1(f->q_OffsetFreq));
|
||||
/* InterFreqNeighCellList */
|
||||
carrier->interFreqNeighCellList = calloc_or_fail(1, sizeof(*carrier->interFreqNeighCellList));
|
||||
return carrier;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Build an NR_SIB4 structure populated with inter-frequency neighbour cell entries.
|
||||
*
|
||||
* Iterates over all configured inter-frequency carriers (excluding the serving cell ARFCN)
|
||||
* and, for each carrier, adds the matching neighbour cells from @p neighbour_config.
|
||||
* Carriers with no matching neighbours are discarded. If no valid carrier is found,
|
||||
* the function returns NULL.
|
||||
*
|
||||
* @param neighbour_config Neighbour cell configuration (cells only).
|
||||
* @param inter_freqs gNB-level inter-frequency configuration.
|
||||
* @param serving_ssb_arfcn SSB ARFCN of the serving cell, used to skip the serving carrier.
|
||||
* @return Allocated NR_SIB4_t on success, NULL if no inter-frequency neighbours are available.
|
||||
*/
|
||||
static NR_SIB4_t *get_sib4_inter_freq_neighbors(const neighbour_cell_configuration_t *neighbour_config,
|
||||
const seq_arr_t *inter_freqs,
|
||||
uint32_t serving_ssb_arfcn)
|
||||
{
|
||||
DevAssert(neighbour_config);
|
||||
|
||||
NR_SIB4_t *sib4 = calloc_or_fail(1, sizeof(*sib4));
|
||||
|
||||
const seq_arr_t *neigh = &neighbour_config->neighbour_cells;
|
||||
|
||||
FOR_EACH_SEQ_ARR (nr_inter_freq_cfg_t *, cfg, inter_freqs) {
|
||||
if (cfg->arfcn == serving_ssb_arfcn)
|
||||
continue;
|
||||
|
||||
NR_InterFreqCarrierFreqInfo_t *carrier = build_inter_freq_carrier_from_cfg(cfg);
|
||||
if (!carrier)
|
||||
continue;
|
||||
|
||||
NR_InterFreqNeighCellList_t *neigh_list = carrier->interFreqNeighCellList;
|
||||
|
||||
FOR_EACH_SEQ_ARR (nr_neighbour_cell_t *, nc, neigh) {
|
||||
if (nc->absoluteFrequencySSB != cfg->arfcn || nc->subcarrierSpacing != cfg->scs)
|
||||
continue;
|
||||
|
||||
if (neigh_list->list.count >= NR_maxCellInter) {
|
||||
LOG_W(NR_RRC,
|
||||
"SIB4: too many inter-frequency neighbors for ARFCN %ld (max %d), skipping neighbour\n",
|
||||
carrier->dl_CarrierFreq,
|
||||
NR_maxCellInter);
|
||||
continue;
|
||||
}
|
||||
|
||||
const nr_neighbour_cell_neighbor_offset_t *offset = &nc->sib4.offset;
|
||||
LOG_I(NR_RRC,
|
||||
"SIB4: added inter-frequency neighbour cell: cellId %ld (PCI %d), ARFCN %d, SCS %d\n",
|
||||
nc->nrcell_id,
|
||||
nc->physicalCellId,
|
||||
cfg->arfcn,
|
||||
cfg->scs);
|
||||
asn1cSequenceAdd(neigh_list->list, NR_InterFreqNeighCellInfo_t, cell);
|
||||
cell->physCellId = nc->physicalCellId;
|
||||
|
||||
/* q_OffsetCell: 0 dB is default */
|
||||
cell->q_OffsetCell = get_q_offset_asn1(offset->q_OffsetCell);
|
||||
|
||||
/* q_RxLevMinOffsetCell / q_QualMinOffsetCell: range 1..8 */
|
||||
if (offset->q_RxLevMinOffsetCell != -1)
|
||||
asn1cCallocOne(cell->q_RxLevMinOffsetCell, offset->q_RxLevMinOffsetCell);
|
||||
if (offset->q_QualMinOffsetCell != -1)
|
||||
asn1cCallocOne(cell->q_QualMinOffsetCell, offset->q_QualMinOffsetCell);
|
||||
}
|
||||
|
||||
if (neigh_list->list.count > 0) {
|
||||
asn1cSeqAdd(&sib4->interFreqCarrierFreqList.list, carrier);
|
||||
} else {
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_InterFreqCarrierFreqInfo, carrier);
|
||||
}
|
||||
}
|
||||
|
||||
if (sib4->interFreqCarrierFreqList.list.count == 0) {
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_SIB4, sib4);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return sib4;
|
||||
}
|
||||
|
||||
/** @brief Return the frequency of the SS block of the cell for which this message is included,
|
||||
* or of other SS blocks within the same carrier. MeasurementTimingConfiguration message is
|
||||
* used to convey assistance information for measurement timing between nodes (e.g F1 setup) */
|
||||
@@ -725,6 +850,30 @@ void rrc_gNB_process_f1_setup_req(f1ap_setup_req_t *req, sctp_assoc_t assoc_id)
|
||||
free_byte_array(enc);
|
||||
LOG_I(NR_RRC, "DU %ld: added SIB3 to F1 Setup Response (cell %ld)\n", du->gNB_DU_id, new->info.cell_id);
|
||||
} break;
|
||||
case NR_SIB_4: {
|
||||
if (!rrc->neighbour_cell_configuration)
|
||||
break;
|
||||
const neighbour_cell_configuration_t *neighbour_cfg = get_cell_neighbour_list(rrc, new);
|
||||
if (!neighbour_cfg)
|
||||
break;
|
||||
|
||||
NR_SIB4_t *sib4 = get_sib4_inter_freq_neighbors(neighbour_cfg, &rrc->inter_freqs, get_ssb_arfcn(new));
|
||||
if (!sib4) {
|
||||
LOG_W(NR_RRC, "SIB4: could not build from neighbours, skipping\n");
|
||||
break;
|
||||
}
|
||||
|
||||
byte_array_t enc = do_SIB4_NR(sib4);
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_SIB4, sib4);
|
||||
if (!enc.buf || enc.len == 0) {
|
||||
free_byte_array(enc);
|
||||
LOG_E(NR_RRC, "SIB4 encoding failed\n");
|
||||
break;
|
||||
}
|
||||
add_si_msg(&cell, sib->SIB_type, &enc);
|
||||
free_byte_array(enc);
|
||||
LOG_I(NR_RRC, "DU %ld: added SIB4 to F1 Setup Response (cell %ld)\n", du->gNB_DU_id, new->info.cell_id);
|
||||
} break;
|
||||
default:
|
||||
AssertFatal(false, "SIB%d not handled yet\n", sib->SIB_type);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user