Commit 817d1a6c authored by Guido Casati's avatar Guido Casati

RRC/MAC: add SIB3 intra-freqeuncy neighbour support

Introduce NR SIB3 encoding in RRC and wire SIB3 neighbour information
into CU–DU F1 Setup and DU SystemInformation. Extend MAC to decode NR
SIB3 from CU-provided SIB containers and attach the decoded SIB3 to the
DU SystemInformation structure.

Changes:
- Add NR_SIB3 ASN.1 include and a do_SIB3_NR() encoder in asn1_msg
  so SIB3 can be serialized into a byte_array_t buffer.
- Introduce get_q_offset_asn1() and get_sib3_intra_freq_neighbors() in
  rrc_gNB_du.c to build an intra-frequency neighbour cell list for SIB3,
  mapping q_OffsetCell dB values to NR_Q_OffsetRange and optionally
  filling q_RxLevMinOffsetCell and q_QualMinOffsetCell.
- Limit the number of intra-frequency neighbours in SIB3 to
  NR_maxCellIntra, logging a warning and skipping additional cells when
  the limit is reached.
- Hook SIB3 construction and encoding into rrc_gNB_process_f1_setup_req()
  so SIB type 3 entries are added to the F1 Setup Response when
  neighbour configuration and matching neighbours are available.
- Handle NR_SIB_3 in nr_mac_configure_other_sib() by validating the CU
  SIB3 container buffer, decoding it with uper_decode into an NR_SIB3_t,
  and attaching the resulting SIB3 to the MAC SystemInformation via
  add_sib_to_systeminformation().
parent c53b28f5
......@@ -1008,6 +1008,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_3: {
struct NR_SystemInformation_IEs__sib_TypeAndInfo__Member *type = calloc_or_fail(1, sizeof(*type));
type->present = NR_SystemInformation_IEs__sib_TypeAndInfo__Member_PR_sib3;
NR_SIB3_t *sib3 = NULL;
asn_dec_rval_t dec_rval = uper_decode(NULL, &asn_DEF_NR_SIB3, (void **)&sib3, 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_SIB3, sib3);
free(type);
break;
}
type->choice.sib3 = sib3;
add_sib_to_systeminformation(sysInfo, type);
break;
}
default :
AssertFatal(false, "Invalid or not supported SIB%d\n", config_sibs[i]);
}
......
......@@ -244,6 +244,27 @@ byte_array_t do_SIB2_NR(const NR_SIB2_t *sib2)
return msg;
}
byte_array_t do_SIB3_NR(const NR_SIB3_t *sib3)
{
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_SIB3, sib3, errbuf, &errlen);
if (ret != 0) {
LOG_E(NR_RRC, "SIB3 constraint check failed: %s\n", errbuf);
return msg;
}
int val = uper_encode_to_new_buffer(&asn_DEF_NR_SIB3, NULL, (void *)sib3, (void **)&msg.buf);
if (val <= 0) {
LOG_E(NR_RRC, "Failed to encode SIB3\n");
return msg;
}
msg.len = val;
return msg;
}
int do_RRCReject(uint8_t *const buffer)
{
asn_enc_rval_t enc_rval;
......
......@@ -34,6 +34,7 @@
#include "NR_UE-NR-Capability.h"
#include "NR_UE-CapabilityRAT-ContainerList.h"
#include "NR_SIB2.h"
#include "NR_SIB3.h"
#include "ds/seq_arr.h"
#include "ds/byte_array.h"
#include "openair2/LAYER2/nr_pdcp/nr_pdcp_configuration.h"
......@@ -67,6 +68,7 @@ typedef struct {
int xer_sprint_NR(char *string, size_t string_size, struct asn_TYPE_descriptor_s *td, void *sptr);
byte_array_t do_SIB2_NR(const NR_SIB2_t *sib2);
byte_array_t do_SIB3_NR(const NR_SIB3_t *sib3);
int do_RRCReject(uint8_t *const buffer);
......
......@@ -24,6 +24,8 @@
#include "ngap_messages_types.h"
#include "nr_rrc_defs.h"
#include "NR_SIB2.h"
#include "NR_SIB3.h"
#include "NR_Q-OffsetRange.h"
#include "openair2/RRC/NR/MESSAGES/asn1_msg.h"
#include "rrc_gNB_mobility.h"
#include "openair2/F1AP/f1ap_common.h"
......@@ -35,6 +37,7 @@
#include "s1ap_messages_types.h"
#include "tree.h"
#include "uper_decoder.h"
#include "common/utils/oai_asn1.h"
#include "utils.h"
#include "xer_encoder.h"
......@@ -172,6 +175,136 @@ static NR_SIB2_t *get_sib2_from_cfg(const sib2_config_t *cfg, NR_SSB_MTC_t *ssbm
return sib2;
}
/** @brief Map internal int enum to ASN.1 Q-OffsetRange value. */
static long get_q_offset_asn1(int q_offset_db)
{
switch (q_offset_db) {
case -24:
return NR_Q_OffsetRange_dB_24;
case -22:
return NR_Q_OffsetRange_dB_22;
case -20:
return NR_Q_OffsetRange_dB_20;
case -18:
return NR_Q_OffsetRange_dB_18;
case -16:
return NR_Q_OffsetRange_dB_16;
case -14:
return NR_Q_OffsetRange_dB_14;
case -12:
return NR_Q_OffsetRange_dB_12;
case -10:
return NR_Q_OffsetRange_dB_10;
case -8:
return NR_Q_OffsetRange_dB_8;
case -6:
return NR_Q_OffsetRange_dB_6;
case -5:
return NR_Q_OffsetRange_dB_5;
case -4:
return NR_Q_OffsetRange_dB_4;
case -3:
return NR_Q_OffsetRange_dB_3;
case -2:
return NR_Q_OffsetRange_dB_2;
case -1:
return NR_Q_OffsetRange_dB_1;
case 0:
return NR_Q_OffsetRange_dB0;
case 1:
return NR_Q_OffsetRange_dB1;
case 2:
return NR_Q_OffsetRange_dB2;
case 3:
return NR_Q_OffsetRange_dB3;
case 4:
return NR_Q_OffsetRange_dB4;
case 5:
return NR_Q_OffsetRange_dB5;
case 6:
return NR_Q_OffsetRange_dB6;
case 8:
return NR_Q_OffsetRange_dB8;
case 10:
return NR_Q_OffsetRange_dB10;
case 12:
return NR_Q_OffsetRange_dB12;
case 14:
return NR_Q_OffsetRange_dB14;
case 16:
return NR_Q_OffsetRange_dB16;
case 18:
return NR_Q_OffsetRange_dB18;
case 20:
return NR_Q_OffsetRange_dB20;
case 22:
return NR_Q_OffsetRange_dB22;
case 24:
return NR_Q_OffsetRange_dB24;
default:
AssertFatal(false, "Invalid q_offset_db value %d\n", q_offset_db);
}
}
/** @brief Build a SIB3 (intra-frequency neighbour cell list) from the configured neighbour cells.
* Only neighbours whose SSB ARFCN matches @param serving_ssb_arfcn are included.
* @param neighbours list of configured neighbour cells
* @param serving_ssb_arfcn SSB ARFCN of the serving cell, used to filter intra-frequency neighbours
* @returns allocated NR_SIB3_t on success, NULL if no matching neighbours were found */
static NR_SIB3_t *get_sib3_intra_freq_neighbors(const seq_arr_t *neighbours, uint32_t serving_ssb_arfcn)
{
DevAssert(neighbours);
NR_SIB3_t *sib3 = calloc_or_fail(1, sizeof(*sib3));
sib3->intraFreqNeighCellList = calloc_or_fail(1, sizeof(*sib3->intraFreqNeighCellList));
int intra_count = 0;
FOR_EACH_SEQ_ARR (const nr_neighbour_cell_t *, neigh, neighbours) {
if (neigh->absoluteFrequencySSB != serving_ssb_arfcn)
continue;
LOG_D(NR_RRC,
"SIB3: intra-frequency neighbour candidate: cell ID %lu, PCI %d (SSB ARFCN %d)\n",
neigh->nrcell_id,
neigh->physicalCellId,
neigh->absoluteFrequencySSB);
NR_IntraFreqNeighCellInfo_t *cell = calloc_or_fail(1, sizeof(*cell));
cell->physCellId = neigh->physicalCellId;
const nr_neighbour_cell_neighbor_offset_t *off = &neigh->sib3.offset;
/* q-OffsetCell: NR_Q-OffsetRange (-24..24 dB), 0 dB default */
cell->q_OffsetCell = get_q_offset_asn1(off->q_OffsetCell);
/* q-RxLevMinOffsetCell / q-QualMinOffsetCell: (1..8) */
if (off->q_RxLevMinOffsetCell != -1)
asn1cCallocOne(cell->q_RxLevMinOffsetCell, off->q_RxLevMinOffsetCell);
if (off->q_QualMinOffsetCell != -1)
asn1cCallocOne(cell->q_QualMinOffsetCell, off->q_QualMinOffsetCell);
/* Add to SIB3 intra-frequency neighbour list */
if (sib3->intraFreqNeighCellList->list.count >= NR_maxCellIntra) {
LOG_W(NR_RRC, "SIB3: too many intra-frequency neighbors (max %d), skipping cell %ld\n", NR_maxCellIntra, neigh->nrcell_id);
asn1cFreeStruc(asn_DEF_NR_IntraFreqNeighCellInfo, cell);
break;
}
asn1cSeqAdd(&sib3->intraFreqNeighCellList->list, cell);
++intra_count;
}
if (intra_count == 0) {
LOG_W(NR_RRC,
"SIB3: no intra-frequency neighbours found for serving SSB ARFCN %u "
"(configured neighbours %u), not building SIB3\n",
serving_ssb_arfcn,
(unsigned)seq_arr_size(neighbours));
asn1cFreeStruc(asn_DEF_NR_SIB3, sib3);
return NULL;
}
return sib3;
}
/** @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) */
......@@ -569,6 +702,29 @@ 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 SIB2 to F1 Setup Response (cell %ld)\n", du->gNB_DU_id, new->info.cell_id);
} break;
case NR_SIB_3: {
if (!rrc->neighbour_cell_configuration)
break;
const neighbour_cell_configuration_t *neighbour_config = get_cell_neighbour_list(rrc, new);
if (!neighbour_config)
break;
const seq_arr_t *neigh_cells = &neighbour_config->neighbour_cells;
NR_SIB3_t *sib3 = get_sib3_intra_freq_neighbors(neigh_cells, get_ssb_arfcn(new));
if (!sib3)
break;
byte_array_t enc = do_SIB3_NR(sib3);
ASN_STRUCT_FREE(asn_DEF_NR_SIB3, sib3);
if (!enc.buf || enc.len == 0) {
free_byte_array(enc);
LOG_E(NR_RRC, "SIB3 encoding failed\n");
break;
}
add_si_msg(&cell, sib->SIB_type, &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);
} break;
default:
AssertFatal(false, "SIB%d not handled yet\n", sib->SIB_type);
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment