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);
|
add_sib_to_systeminformation(sysInfo, type);
|
||||||
break;
|
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 :
|
default :
|
||||||
AssertFatal(false, "Invalid or not supported SIB%d\n", config_sibs[i]);
|
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;
|
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)
|
int do_RRCReject(uint8_t *const buffer)
|
||||||
{
|
{
|
||||||
asn_enc_rval_t enc_rval;
|
asn_enc_rval_t enc_rval;
|
||||||
|
|||||||
@@ -35,6 +35,7 @@
|
|||||||
#include "NR_UE-CapabilityRAT-ContainerList.h"
|
#include "NR_UE-CapabilityRAT-ContainerList.h"
|
||||||
#include "NR_SIB2.h"
|
#include "NR_SIB2.h"
|
||||||
#include "NR_SIB3.h"
|
#include "NR_SIB3.h"
|
||||||
|
#include "NR_SIB4.h"
|
||||||
#include "ds/seq_arr.h"
|
#include "ds/seq_arr.h"
|
||||||
#include "ds/byte_array.h"
|
#include "ds/byte_array.h"
|
||||||
#include "openair2/LAYER2/nr_pdcp/nr_pdcp_configuration.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_SIB2_NR(const NR_SIB2_t *sib2);
|
||||||
byte_array_t do_SIB3_NR(const NR_SIB3_t *sib3);
|
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);
|
int do_RRCReject(uint8_t *const buffer);
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
#include "nr_rrc_defs.h"
|
#include "nr_rrc_defs.h"
|
||||||
#include "NR_SIB2.h"
|
#include "NR_SIB2.h"
|
||||||
#include "NR_SIB3.h"
|
#include "NR_SIB3.h"
|
||||||
|
#include "NR_SIB4.h"
|
||||||
#include "NR_Q-OffsetRange.h"
|
#include "NR_Q-OffsetRange.h"
|
||||||
#include "openair2/RRC/NR/MESSAGES/asn1_msg.h"
|
#include "openair2/RRC/NR/MESSAGES/asn1_msg.h"
|
||||||
#include "rrc_gNB_mobility.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;
|
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,
|
/** @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
|
* 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) */
|
* 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);
|
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);
|
LOG_I(NR_RRC, "DU %ld: added SIB3 to F1 Setup Response (cell %ld)\n", du->gNB_DU_id, new->info.cell_id);
|
||||||
} break;
|
} 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:
|
default:
|
||||||
AssertFatal(false, "SIB%d not handled yet\n", sib->SIB_type);
|
AssertFatal(false, "SIB%d not handled yet\n", sib->SIB_type);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user