From 9afa94040dc98acaa2d8b376d4b1a71f902c36d8 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Wed, 11 Feb 2026 10:33:56 +0100 Subject: [PATCH] RRC/SIB2: load cell reselection config from gNB Introduce a typed SIB2 cell reselection configuration, load it from the gNB configuration into the RRC gNB instance, and use it to build and encode SIB2 instead of relying on a fixed SIB2 definition. Validate SIB2 parameters against explicit bounds and make the SIB2 encoder consume a pre-built ASN.1 structure with safer error handling. Changes: - Add SIB2 mobility timer and q-HystSF enums plus SIB2 config structs (`sib2_speed_state_reselection_pars_t`, `sib2_config_t`) and a `sib2_config` field on `gNB_RRC_INST` in `nr_rrc_defs.h`. - Define SIB2 configuration option names, defaults (`GNB_CONFIG_STRING_SIB2_*`, `GNBSIB2PARAMS_DESC`) in `gnb_paramdef.h` for q_Hyst, thresholds, mobility timers, scaling factors and deriveSSB_IndexFromCell. - Add `sib2_config` parameter keys, defaults, and range/value checks in `gnb_paramdef.h` (`GNBSIB2PARAMS_DESC`). - Add SIB2 `fill_sib2_configuration()` in `gnb_config.c` to read SIB2 parameters from the config file log the resulting configuration and store it in `rrc->sib2_config` via `RCconfig_NRRRC()`. - Add `get_q_hyst_asn1()` and `get_sib2_from_cfg()` in `rrc_gNB_du.c`, and update `rrc_gNB_process_f1_setup_req()` to encode/add SIB2 from config with explicit encode-failure handling and logging. - Change `do_SIB2_NR` signature in `asn1_msg.h/.c` to `byte_array_t do_SIB2_NR(const NR_SIB2_t *sib2)`, remove internal default SIB2 construction, and add ASN.1 constraint/encode error logging. --- openair2/GNB_APP/gnb_config.c | 61 +++++++++++++ openair2/GNB_APP/gnb_paramdef.h | 90 +++++++++++++++++++ openair2/RRC/NR/MESSAGES/asn1_msg.c | 48 ++++------ openair2/RRC/NR/MESSAGES/asn1_msg.h | 3 +- openair2/RRC/NR/nr_rrc_defs.h | 97 ++++++++++++++++++++ openair2/RRC/NR/rrc_gNB_du.c | 131 +++++++++++++++++++++++++++- 6 files changed, 395 insertions(+), 35 deletions(-) diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index 6c5d9f4781..5980666cff 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -2171,6 +2171,64 @@ static void fill_measurement_configuration(uint8_t gnb_idx, gNB_RRC_INST *rrc) } } +/** @brief Parse and populate SIB2 configuration from config file + * @param[in] gnb_idx gNB index + * @return populated SIB2 configuration */ +static sib2_config_t fill_sib2_configuration(uint8_t gnb_idx) +{ + sib2_config_t sib2 = {0}; + + char sib2_path[MAX_OPTNAME_SIZE * 2 + 8]; + snprintf(sib2_path, sizeof(sib2_path), "%s.[%i].%s", GNB_CONFIG_STRING_GNB_LIST, gnb_idx, GNB_CONFIG_STRING_SIB2_CONFIG); + + GET_PARAMS(SIB2Params, GNBSIB2PARAMS_DESC, sib2_path); + const int n_sib2_params = sizeofArray(SIB2Params); + + cell_reselection_info_common_t *common = &sib2.cell_reselection_info_common; + common->q_Hyst = *gpd(SIB2Params, n_sib2_params, GNB_CONFIG_STRING_SIB2_Q_HYST)->iptr; + /* speedStateReselectionPars */ + common->speedStateReselectionPars = calloc_or_fail(1, sizeof(*common->speedStateReselectionPars)); + sib2_speed_state_reselection_pars_t *speed = common->speedStateReselectionPars; + + /* Cell reselection serving frequency information */ + cell_reselection_serving_freq_info_t *serv = &sib2.cell_reselection_serving_freq_info; + serv->cellReselectionPriority = *gpd(SIB2Params, n_sib2_params, GNB_CONFIG_STRING_SIB2_CELLRESEL_PRIORITY)->iptr; + serv->threshServingLowP = *gpd(SIB2Params, n_sib2_params, GNB_CONFIG_STRING_SIB2_THRESH_SERVING_LOW_P)->iptr; + serv->threshServingLowQ = *gpd(SIB2Params, n_sib2_params, GNB_CONFIG_STRING_SIB2_THRESH_SERVING_LOW_Q)->iptr; + serv->s_NonIntraSearchP = *gpd(SIB2Params, n_sib2_params, GNB_CONFIG_STRING_SIB2_S_NONINTRASEARCH_P)->iptr; + serv->s_NonIntraSearchQ = *gpd(SIB2Params, n_sib2_params, GNB_CONFIG_STRING_SIB2_S_NONINTRASEARCH_Q)->iptr; + + /* Intra-frequency cell reselection parameters */ + intra_freq_cell_reselection_info_t *intra = &sib2.intra_freq_cell_reselection_info; + intra->q_RxLevMin = *gpd(SIB2Params, n_sib2_params, GNB_CONFIG_STRING_SIB2_Q_RXLEVMIN)->iptr; + intra->q_QualMin = *gpd(SIB2Params, n_sib2_params, GNB_CONFIG_STRING_SIB2_Q_QUALMIN)->iptr; + intra->s_IntraSearchP = *gpd(SIB2Params, n_sib2_params, GNB_CONFIG_STRING_SIB2_S_INTRASEARCH_P)->iptr; + intra->s_IntraSearchQ = *gpd(SIB2Params, n_sib2_params, GNB_CONFIG_STRING_SIB2_S_INTRASEARCH_Q)->iptr; + intra->t_ReselectionNR = *gpd(SIB2Params, n_sib2_params, GNB_CONFIG_STRING_SIB2_T_RESEL_NR)->iptr; + + /* deriveSSB_IndexFromCell (0/1) */ + int derive_ssb = *gpd(SIB2Params, n_sib2_params, GNB_CONFIG_STRING_SIB2_DERIVE_SSB_IDX)->iptr; + sib2.deriveSSB_IndexFromCell = (derive_ssb != 0); + + /* Speed-state pars: optional parameters are omitted from the config file (default applied), not set to -1. */ + speed->t_Evaluation = *gpd(SIB2Params, n_sib2_params, GNB_CONFIG_STRING_SIB2_SPEED_T_EVAL)->iptr; + speed->t_HystNormal = *gpd(SIB2Params, n_sib2_params, GNB_CONFIG_STRING_SIB2_SPEED_T_HYST_NORMAL)->iptr; + speed->n_CellChangeMedium = *gpd(SIB2Params, n_sib2_params, GNB_CONFIG_STRING_SIB2_SPEED_N_CELL_CHG_MED)->iptr; + speed->n_CellChangeHigh = *gpd(SIB2Params, n_sib2_params, GNB_CONFIG_STRING_SIB2_SPEED_N_CELL_CHG_HIGH)->iptr; + speed->sf_Medium = *gpd(SIB2Params, n_sib2_params, GNB_CONFIG_STRING_SIB2_SPEED_SF_MEDIUM)->iptr; + speed->sf_High = *gpd(SIB2Params, n_sib2_params, GNB_CONFIG_STRING_SIB2_SPEED_SF_HIGH)->iptr; + + LOG_I(GNB_APP, + "gNB %d SIB2 config: q_Hyst=%d dB, cellReselPrio=%d, q_RxLevMin=%d dBm, t_ReselectionNR=%d s\n", + gnb_idx, + common->q_Hyst, + serv->cellReselectionPriority, + intra->q_RxLevMin, + intra->t_ReselectionNR); + + return sib2; +} + /** * @brief Allocates and initializes RRC instances * Currently assuming 1 instance @@ -2270,6 +2328,9 @@ gNB_RRC_INST *RCconfig_NRRRC() }// }//End for (k=0; k sib2_config = fill_sib2_configuration(i); }//End if (num_gnbs>0) if (!NODE_IS_DU(rrc->node_type)) diff --git a/openair2/GNB_APP/gnb_paramdef.h b/openair2/GNB_APP/gnb_paramdef.h index 50184503ea..98e53d83e2 100644 --- a/openair2/GNB_APP/gnb_paramdef.h +++ b/openair2/GNB_APP/gnb_paramdef.h @@ -10,6 +10,7 @@ #define __GNB_APP_GNB_PARAMDEF__H__ #include "common/config/config_paramdesc.h" +#include "common/config/config_userapi.h" #include "common/ngran_types.h" #include "RRC_nr_paramsvalues.h" @@ -374,6 +375,95 @@ typedef enum { #define MEASUREMENT_EVENTS_INCLUDE_BEAM_MEAS_IDX 1 #define MEASUREMENT_EVENTS_MAX_RS_INDEX_TO_REPORT 2 +/*-------------------------------------------------------------------------------------------------------------------------------------*/ +/* SIB2 cell reselection configuration parameters */ +/*-------------------------------------------------------------------------------------------------------------------------------------*/ + +#define GNB_CONFIG_STRING_SIB2_CONFIG "sib2_config" + +/* clang-format off */ + +/* SIB2 top-level parameters (per cell) */ +#define GNB_CONFIG_STRING_SIB2_Q_HYST "q_Hyst" +#define GNB_CONFIG_STRING_SIB2_CELLRESEL_PRIORITY "cellReselectionPriority" +#define GNB_CONFIG_STRING_SIB2_THRESH_SERVING_LOW_P "threshServingLowP" +#define GNB_CONFIG_STRING_SIB2_THRESH_SERVING_LOW_Q "threshServingLowQ" +#define GNB_CONFIG_STRING_SIB2_S_NONINTRASEARCH_P "s_NonIntraSearchP" +#define GNB_CONFIG_STRING_SIB2_S_NONINTRASEARCH_Q "s_NonIntraSearchQ" +#define GNB_CONFIG_STRING_SIB2_Q_RXLEVMIN "q_RxLevMin" +#define GNB_CONFIG_STRING_SIB2_Q_QUALMIN "q_QualMin" +#define GNB_CONFIG_STRING_SIB2_S_INTRASEARCH_P "s_IntraSearchP" +#define GNB_CONFIG_STRING_SIB2_S_INTRASEARCH_Q "s_IntraSearchQ" +#define GNB_CONFIG_STRING_SIB2_T_RESEL_NR "t_ReselectionNR" +#define GNB_CONFIG_STRING_SIB2_DERIVE_SSB_IDX "deriveSSB_IndexFromCell" + +/* SIB2 speedStateReselectionPars parameters */ +#define GNB_CONFIG_STRING_SIB2_SPEED_T_EVAL "speed_t_Evaluation" +#define GNB_CONFIG_STRING_SIB2_SPEED_T_HYST_NORMAL "speed_t_HystNormal" +#define GNB_CONFIG_STRING_SIB2_SPEED_N_CELL_CHG_MED "speed_n_CellChangeMedium" +#define GNB_CONFIG_STRING_SIB2_SPEED_N_CELL_CHG_HIGH "speed_n_CellChangeHigh" +#define GNB_CONFIG_STRING_SIB2_SPEED_SF_MEDIUM "speed_sf_Medium" +#define GNB_CONFIG_STRING_SIB2_SPEED_SF_HIGH "speed_sf_High" + +#define GNBSIB2PARAMS_DESC { /* optname helpstr paramflags XXXptr def val type numelt */ \ + {GNB_CONFIG_STRING_SIB2_Q_HYST, \ + "SIB2 q-Hyst (0,1,2,3,4,5,6,8,10..24 dB)", 0, .iptr=NULL, .defintval=0, TYPE_INT, 0, \ + .chkPptr = &(checkedparam_t){.s1 = {config_check_intval, {0, 1, 2, 3, 4, 5, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24}, 16}}}, \ + {GNB_CONFIG_STRING_SIB2_CELLRESEL_PRIORITY, \ + "SIB2 cellReselectionPriority (0..7)", 0, .iptr=NULL, .defintval=0, TYPE_INT, 0, \ + .chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {0, 7}}}}, \ + {GNB_CONFIG_STRING_SIB2_THRESH_SERVING_LOW_P, \ + "SIB2 threshServingLowP (0..31)", 0, .iptr=NULL, .defintval=0, TYPE_INT, 0, \ + .chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {0, 31}}}}, \ + {GNB_CONFIG_STRING_SIB2_THRESH_SERVING_LOW_Q, \ + "SIB2 threshServingLowQ (0..31, -1=disabled)", 0, .iptr=NULL, .defintval=0, TYPE_INT, 0, \ + .chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {-1, 31}}}}, \ + {GNB_CONFIG_STRING_SIB2_S_NONINTRASEARCH_P, \ + "SIB2 s-NonIntraSearchP (0..31, -1=disabled)", 0, .iptr=NULL, .defintval=-1, TYPE_INT, 0, \ + .chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {-1, 31}}}}, \ + {GNB_CONFIG_STRING_SIB2_S_NONINTRASEARCH_Q, \ + "SIB2 s-NonIntraSearchQ (0..31, -1=disabled)", 0, .iptr=NULL, .defintval=-1, TYPE_INT, 0, \ + .chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {-1, 31}}}}, \ + {GNB_CONFIG_STRING_SIB2_Q_RXLEVMIN, \ + "SIB2 q-RxLevMin (-70..-22)", 0, .iptr=NULL, .defintval=-56, TYPE_INT, 0, \ + .chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {-70, -22}}}}, \ + {GNB_CONFIG_STRING_SIB2_Q_QUALMIN, \ + "SIB2 q-QualMin (-43..-12 or -1=disabled)", 0, .iptr=NULL, .defintval=-1, TYPE_INT, 0, \ + .chkPptr = &(checkedparam_t){.s1 = {config_check_intval, \ + {-1, -43, -42, -41, -40, -39, -38, -37, -36, -35, -34, -33, -32, -31, -30, -29, -28, \ + -27, -26, -25, -24, -23, -22, -21, -20, -19, -18, -17, -16, -15, -14, -13, -12}, 33}}}, \ + {GNB_CONFIG_STRING_SIB2_S_INTRASEARCH_P, \ + "SIB2 s-IntraSearchP (0..31)", 0, .iptr=NULL, .defintval=22, TYPE_INT, 0, \ + .chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {0, 31}}}}, \ + {GNB_CONFIG_STRING_SIB2_S_INTRASEARCH_Q, \ + "SIB2 s-IntraSearchQ (0..31, -1=disabled)", 0, .iptr=NULL, .defintval=-1, TYPE_INT, 0, \ + .chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {-1, 31}}}}, \ + {GNB_CONFIG_STRING_SIB2_T_RESEL_NR, \ + "SIB2 t-ReselectionNR (0..7)", 0, .iptr=NULL, .defintval=1, TYPE_INT, 0, \ + .chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {0, 7}}}}, \ + {GNB_CONFIG_STRING_SIB2_DERIVE_SSB_IDX, \ + "SIB2 deriveSSB-IndexFromCell (0/1)", PARAMFLAG_BOOL, .iptr=NULL, .defintval=1, TYPE_INT, 0}, \ + {GNB_CONFIG_STRING_SIB2_SPEED_T_EVAL, \ + "SIB2 speed t-Evaluation (0=s30,1=s60,2=s120,3=s180,4=s240)", 0, .iptr=NULL, .defintval=0, TYPE_INT, 0, \ + .chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {0, 4}}}}, \ + {GNB_CONFIG_STRING_SIB2_SPEED_T_HYST_NORMAL, \ + "SIB2 speed t-HystNormal (0=s30,1=s60,2=s120,3=s180,4=s240)", 0, .iptr=NULL, .defintval=0, TYPE_INT, 0, \ + .chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {0, 4}}}}, \ + {GNB_CONFIG_STRING_SIB2_SPEED_N_CELL_CHG_MED, \ + "SIB2 speed n-CellChangeMedium (1..16)", 0, .iptr=NULL, .defintval=1, TYPE_INT, 0, \ + .chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {1, 16}}}}, \ + {GNB_CONFIG_STRING_SIB2_SPEED_N_CELL_CHG_HIGH, \ + "SIB2 speed n-CellChangeHigh (1..16)", 0, .iptr=NULL, .defintval=2, TYPE_INT, 0, \ + .chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {1, 16}}}}, \ + {GNB_CONFIG_STRING_SIB2_SPEED_SF_MEDIUM, \ + "SIB2 speed sf-Medium (0=dB-6,1=dB-4,2=dB-2,3=dB0)", 0, .iptr=NULL, .defintval=1, TYPE_INT, 0, \ + .chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {0, 3}}}}, \ + {GNB_CONFIG_STRING_SIB2_SPEED_SF_HIGH, \ + "SIB2 speed sf-High (0=dB-6,1=dB-4,2=dB-2,3=dB0)", 0, .iptr=NULL, .defintval=0, TYPE_INT, 0, \ + .chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {0, 3}}}}, \ +} +/* clang-format on */ + /* PLMN ID configuration */ #define GNB_CONFIG_STRING_PLMN_LIST "plmn_list" diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.c b/openair2/RRC/NR/MESSAGES/asn1_msg.c index 8d5d00c975..b8cd1ab284 100644 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.c +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.c @@ -223,39 +223,25 @@ NR_UE_NR_Capability_t *decode_nr_ue_capability(int rnti, const NR_UE_CapabilityR return NULL; } -//------------------------------------------------------------------------------ - -int do_SIB2_NR(uint8_t **msg_SIB2, NR_SSB_MTC_t *ssbmtc) +byte_array_t do_SIB2_NR(const NR_SIB2_t *sib2) { - NR_SIB2_t *sib2 = calloc(1, sizeof(*sib2)); - sib2->cellReselectionInfoCommon.q_Hyst = NR_SIB2__cellReselectionInfoCommon__q_Hyst_dB0; - struct NR_SIB2__cellReselectionInfoCommon__speedStateReselectionPars *speed = calloc(1, sizeof(*speed)); - NR_MobilityStateParameters_t mobilityStateParameters = {0}; - mobilityStateParameters.t_Evaluation = NR_MobilityStateParameters__t_Evaluation_s30; - mobilityStateParameters.t_HystNormal = NR_MobilityStateParameters__t_HystNormal_s30; - mobilityStateParameters.n_CellChangeMedium = 1; - mobilityStateParameters.n_CellChangeHigh = 2; - speed->mobilityStateParameters = mobilityStateParameters; - struct NR_SIB2__cellReselectionInfoCommon__speedStateReselectionPars__q_HystSF qhyst = {0}; - qhyst.sf_Medium = NR_SIB2__cellReselectionInfoCommon__speedStateReselectionPars__q_HystSF__sf_Medium_dB_4; - qhyst.sf_High = NR_SIB2__cellReselectionInfoCommon__speedStateReselectionPars__q_HystSF__sf_High_dB_6; - speed->q_HystSF = qhyst; - sib2->cellReselectionInfoCommon.speedStateReselectionPars = speed; - sib2->cellReselectionServingFreqInfo.cellReselectionPriority = 0; // INTEGER (0..7) - sib2->cellReselectionServingFreqInfo.threshServingLowP = 0; - NR_ReselectionThresholdQ_t *threshServingLowQ = calloc(1, sizeof(*threshServingLowQ)); - *threshServingLowQ = 0; - sib2->cellReselectionServingFreqInfo.threshServingLowQ = threshServingLowQ; - sib2->intraFreqCellReselectionInfo.q_RxLevMin = -56; // INTEGER (-70..-22) - sib2->intraFreqCellReselectionInfo.s_IntraSearchP = 22; // INTEGER (0..31) - sib2->intraFreqCellReselectionInfo.t_ReselectionNR = 1; // INTEGER (0..7) - sib2->intraFreqCellReselectionInfo.deriveSSB_IndexFromCell = true; - sib2->intraFreqCellReselectionInfo.smtc = ssbmtc; + 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_SIB2, sib2, errbuf, &errlen); + if (ret != 0) { + LOG_E(NR_RRC, "SIB2 constraint check failed: %s\n", errbuf); + return msg; + } - ssize_t size = uper_encode_to_new_buffer(&asn_DEF_NR_SIB2, NULL, (void *)sib2, (void **)msg_SIB2); - AssertFatal (size > 0, "ASN1 message encoding failed (encoded %lu bytes)!\n", size); - ASN_STRUCT_FREE(asn_DEF_NR_SIB2, sib2); - return size; + int val = uper_encode_to_new_buffer(&asn_DEF_NR_SIB2, NULL, (void *)sib2, (void **)&msg.buf); + if (val <= 0) { + LOG_E(NR_RRC, "Failed to encode SIB2\n"); + return msg; + } + + msg.len = val; + return msg; } int do_RRCReject(uint8_t *const buffer) diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.h b/openair2/RRC/NR/MESSAGES/asn1_msg.h index 7aeb2b4ed0..43f4e7dcef 100644 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.h +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.h @@ -33,6 +33,7 @@ #include "NR_MeasurementTimingConfiguration.h" #include "NR_UE-NR-Capability.h" #include "NR_UE-CapabilityRAT-ContainerList.h" +#include "NR_SIB2.h" #include "ds/seq_arr.h" #include "ds/byte_array.h" #include "openair2/LAYER2/nr_pdcp/nr_pdcp_configuration.h" @@ -65,7 +66,7 @@ typedef struct { */ int xer_sprint_NR(char *string, size_t string_size, struct asn_TYPE_descriptor_s *td, void *sptr); -int do_SIB2_NR(uint8_t **msg_SIB2, NR_SSB_MTC_t *ssbmtc); +byte_array_t do_SIB2_NR(const NR_SIB2_t *sib2); int do_RRCReject(uint8_t *const buffer); diff --git a/openair2/RRC/NR/nr_rrc_defs.h b/openair2/RRC/NR/nr_rrc_defs.h index 3b07ec6a6c..68104ba203 100644 --- a/openair2/RRC/NR/nr_rrc_defs.h +++ b/openair2/RRC/NR/nr_rrc_defs.h @@ -418,6 +418,101 @@ typedef struct nr_rrc_cuup_container_t { sctp_assoc_t assoc_id; } nr_rrc_cuup_container_t; +/**Timers for SIB2 MobilityStateParameters (TS 38.331) + * Value set shared by T-Evaluation and T-HystNormal: (s30, s60, s120, s180, s240). */ +typedef enum sib2_mobility_state_timer_e { + SIB2_MOBILITY_STATE_TIMER_S30, + SIB2_MOBILITY_STATE_TIMER_S60, + SIB2_MOBILITY_STATE_TIMER_S120, + SIB2_MOBILITY_STATE_TIMER_S180, + SIB2_MOBILITY_STATE_TIMER_S240, +} sib2_mobility_state_timer_t; + +/** Q-HystSF scaling factors for Q_hyst (TS 38.331) + * Speed-dependent offsets (-6, -4, -2, 0 dB) applied to the base Q_hyst + * when the UE is in medium or high mobility state. */ +typedef enum sib2_q_hystsf_e { + SIB2_Q_HYSTSF_DB_6, + SIB2_Q_HYSTSF_DB_4, + SIB2_Q_HYSTSF_DB_2, + SIB2_Q_HYSTSF_DB0, +} sib2_q_hystsf_t; + +/** SIB2 speedStateReselectionPars configuration + * Mirrors TS 38.331 MobilityStateParameters and Q-hystSF. */ +typedef struct sib2_speed_state_reselection_pars_s { + /* t-Evaluation: duration for evaluating allowed reselections + * to enter mobility states (s30, s60, s120, s180, s240) */ + sib2_mobility_state_timer_t t_Evaluation; + /* t-HystNormal: additional time period to evaluate reselection criteria + * before returning to normal mobility (s30, s60, s120, s180, s240) */ + sib2_mobility_state_timer_t t_HystNormal; + /* n-CellChangeMedium: max number of cell reselections to enter medium mobility state (1..16) */ + int n_CellChangeMedium; + /* n-CellChangeHigh: max number of cell reselections to enter high mobility state (1..16) */ + int n_CellChangeHigh; + /* sf-Medium: speed-dependent scaling factor for Qhyst in medium state (-6, -4, -2, 0 dB) */ + sib2_q_hystsf_t sf_Medium; + /* sf-High: speed-dependent scaling factor for Qhyst in high state (-6, -4, -2, 0 dB) */ + sib2_q_hystsf_t sf_High; +} sib2_speed_state_reselection_pars_t; + +/** SIB2 cellReselectionServingFreqInfo (TS 38.331) */ +typedef struct { + /* s-NonIntraSearchP: Srxlev threshold to trigger NR inter-freq / inter-RAT measurements (0..31)*2 dB. + * Set to -1 to omit (optional ASN.1 field). */ + int s_NonIntraSearchP; + /* s-NonIntraSearchQ: Squal threshold to trigger NR inter-freq / inter-RAT measurements (0..31)*2 dB */ + int s_NonIntraSearchQ; + /* threshServingLowP: Srxlev threshold (dB) used by the UE on the serving cell + * when reselecting towards a lower-priority RAT/frequency (0..31)*2 dB */ + int threshServingLowP; + /* threshServingLowQ: Squal threshold (dB) used by the UE on the serving cell + * when reselecting towards a lower-priority RAT/frequency (0..31)*2 dB. + * Set to -1 to omit (optional ASN.1 field). */ + int threshServingLowQ; + /* cellReselectionPriority: absolute priority of serving NR frequency (0..7) */ + int cellReselectionPriority; +} cell_reselection_serving_freq_info_t; + +/** SIB2 cellReselectionInfoCommon (TS 38.331) */ +typedef struct { + /* q-Hyst: hysteresis added to serving-cell ranking R_s (0..24 dB) */ + int q_Hyst; + /* speedStateReselectionPars: MobilityStateParameters + q-HystSF */ + sib2_speed_state_reselection_pars_t *speedStateReselectionPars; +} cell_reselection_info_common_t; + +/** SIB2 intraFreqCellReselectionInfo (TS 38.331) */ +typedef struct { + /* q-RxLevMin: minimum required RX level in the cell (dBm) */ + int q_RxLevMin; + /* q-QualMin: minimum required quality level in the cell (dB); */ + int q_QualMin; + /* s-IntraSearchP: Srxlev threshold for intra-freq meas (0..31) */ + int s_IntraSearchP; + /* s-IntraSearchQ: Squal threshold for intra-freq meas (0..31) */ + int s_IntraSearchQ; + /* t-ReselectionNR: NR cell reselection timer (0..7) */ + int t_ReselectionNR; +} intra_freq_cell_reselection_info_t; + +/** SIB2 configuration for idle/inactive cell reselection + * (maps to SIB2 reselection parameters in TS 38.304/38.331, per cell). + * @note Squal = Cell selection quality value (dB) + * Srxlev = Cell selection RX level value (dB) */ +typedef struct sib2_config_s { + // cellReselectionInfoCommon + cell_reselection_info_common_t cell_reselection_info_common; + // cellReselectionServingFreqInfo + cell_reselection_serving_freq_info_t cell_reselection_serving_freq_info; + // intraFreqCellReselectionInfo + intra_freq_cell_reselection_info_t intra_freq_cell_reselection_info; + /* deriveSSB_IndexFromCell: SIB2 deriveSSB-IndexFromCell - whether UE may + * assume SFN/SSB alignment across cells on serving freq (per TS 38.304/38.133) */ + bool deriveSSB_IndexFromCell; +} sib2_config_t; + //---NR---(completely change)--------------------- typedef struct gNB_RRC_INST_s { @@ -432,6 +527,8 @@ typedef struct gNB_RRC_INST_s { // RRC configuration nr_rrc_config_t configuration; seq_arr_t *SIBs; + // SIB2 configuration for cell reselection parameters + sib2_config_t sib2_config; // gNB N3 GTPU instance instance_t e1_inst; diff --git a/openair2/RRC/NR/rrc_gNB_du.c b/openair2/RRC/NR/rrc_gNB_du.c index 495ad92c82..d5d0f27be9 100644 --- a/openair2/RRC/NR/rrc_gNB_du.c +++ b/openair2/RRC/NR/rrc_gNB_du.c @@ -23,6 +23,8 @@ #include "f1ap_messages_types.h" #include "ngap_messages_types.h" #include "nr_rrc_defs.h" +#include "NR_SIB2.h" +#include "openair2/RRC/NR/MESSAGES/asn1_msg.h" #include "rrc_gNB_mobility.h" #include "openair2/F1AP/f1ap_common.h" #include "openair2/F1AP/f1ap_ids.h" @@ -55,6 +57,121 @@ static NR_SSB_MTC_t *get_ssb_mtc(const NR_MeasurementTimingConfiguration_t *mtc) return ssb_mtc; } +/** @brief Map config q_Hyst (dB: 0,1,2,3,4,5,6,8,10..24) to ASN.1 q-Hyst enum index (0..15). + * ASN.1 NR_SIB2 cellReselectionInfoCommon.q-Hyst is encoded with 16 values. */ +static long get_q_hyst_asn1(int q_hyst_db) +{ + switch (q_hyst_db) { + case 0: + return NR_SIB2__cellReselectionInfoCommon__q_Hyst_dB0; + case 1: + return NR_SIB2__cellReselectionInfoCommon__q_Hyst_dB1; + case 2: + return NR_SIB2__cellReselectionInfoCommon__q_Hyst_dB2; + case 3: + return NR_SIB2__cellReselectionInfoCommon__q_Hyst_dB3; + case 4: + return NR_SIB2__cellReselectionInfoCommon__q_Hyst_dB4; + case 5: + return NR_SIB2__cellReselectionInfoCommon__q_Hyst_dB5; + case 6: + return NR_SIB2__cellReselectionInfoCommon__q_Hyst_dB6; + case 8: + return NR_SIB2__cellReselectionInfoCommon__q_Hyst_dB8; + case 10: + return NR_SIB2__cellReselectionInfoCommon__q_Hyst_dB10; + case 12: + return NR_SIB2__cellReselectionInfoCommon__q_Hyst_dB12; + case 14: + return NR_SIB2__cellReselectionInfoCommon__q_Hyst_dB14; + case 16: + return NR_SIB2__cellReselectionInfoCommon__q_Hyst_dB16; + case 18: + return NR_SIB2__cellReselectionInfoCommon__q_Hyst_dB18; + case 20: + return NR_SIB2__cellReselectionInfoCommon__q_Hyst_dB20; + case 22: + return NR_SIB2__cellReselectionInfoCommon__q_Hyst_dB22; + case 24: + return NR_SIB2__cellReselectionInfoCommon__q_Hyst_dB24; + default: + AssertFatal(false, "unexpected q-Hyst value %d (configuration should have been validated)\n", q_hyst_db); + } +} + +/** @brief Build NR_SIB2 from validated SIB2 configuration. + * @param cfg Validated SIB2 configuration from config file + * @param ssbmtc SSB-MTC instance to attach to intraFreqCellReselectionInfo.smtc. */ +static NR_SIB2_t *get_sib2_from_cfg(const sib2_config_t *cfg, NR_SSB_MTC_t *ssbmtc) +{ + NR_SIB2_t *sib2 = calloc_or_fail(1, sizeof(*sib2)); + typeof(sib2->cellReselectionInfoCommon) *common = &sib2->cellReselectionInfoCommon; + const cell_reselection_info_common_t *cfg_common = &cfg->cell_reselection_info_common; + + /* q-Hyst: ASN.1 enum (0..15) converted from config dB (0,1,2,3,4,5,6,8,10..24). */ + common->q_Hyst = get_q_hyst_asn1(cfg_common->q_Hyst); + + /* speedStateReselectionPars */ + if (cfg_common->speedStateReselectionPars != NULL) { + struct NR_SIB2__cellReselectionInfoCommon__speedStateReselectionPars *speed = calloc_or_fail(1, sizeof(*speed)); + NR_MobilityStateParameters_t mobilityStateParameters = {0}; + const sib2_speed_state_reselection_pars_t *pars = cfg_common->speedStateReselectionPars; + /* t-Evaluation / t-HystNormal: (0..7) */ + mobilityStateParameters.t_Evaluation = pars->t_Evaluation; + mobilityStateParameters.t_HystNormal = pars->t_HystNormal; + /* n-CellChangeMedium / n-CellChangeHigh: (1..16) */ + mobilityStateParameters.n_CellChangeMedium = pars->n_CellChangeMedium; + mobilityStateParameters.n_CellChangeHigh = pars->n_CellChangeHigh; + speed->mobilityStateParameters = mobilityStateParameters; + + /* q-HystSF: sf-Medium/sf-High (0..3) */ + struct NR_SIB2__cellReselectionInfoCommon__speedStateReselectionPars__q_HystSF qhyst = {0}; + qhyst.sf_Medium = pars->sf_Medium; + qhyst.sf_High = pars->sf_High; + speed->q_HystSF = qhyst; + + common->speedStateReselectionPars = speed; + } + + /* cellReselectionServingFreqInfo */ + typeof(sib2->cellReselectionServingFreqInfo) *serv = &sib2->cellReselectionServingFreqInfo; + const cell_reselection_serving_freq_info_t *cfg_serv = &cfg->cell_reselection_serving_freq_info; + /* cellReselectionPriority: CellReselectionPriority (0..7) */ + serv->cellReselectionPriority = cfg_serv->cellReselectionPriority; + /* threshServingLowP: ReselectionThreshold (0..31) */ + serv->threshServingLowP = cfg_serv->threshServingLowP; + /* threshServingLowQ: ReselectionThresholdQ (0..31) */ + if (cfg_serv->threshServingLowQ != -1) + asn1cCallocOne(serv->threshServingLowQ, cfg_serv->threshServingLowQ); + /* s-NonIntraSearchP/Q: ReselectionThreshold/ReselectionThresholdQ (0..31) */ + if (cfg_serv->s_NonIntraSearchP != -1) + asn1cCallocOne(serv->s_NonIntraSearchP, cfg_serv->s_NonIntraSearchP); + if (cfg_serv->s_NonIntraSearchQ != -1) + asn1cCallocOne(serv->s_NonIntraSearchQ, cfg_serv->s_NonIntraSearchQ); + + /* intraFreqCellReselectionInfo */ + typeof(sib2->intraFreqCellReselectionInfo) *intra = &sib2->intraFreqCellReselectionInfo; + const intra_freq_cell_reselection_info_t *cfg_intra = &cfg->intra_freq_cell_reselection_info; + /* q-RxLevMin: Q-RxLevMin (-70..-22) */ + intra->q_RxLevMin = cfg_intra->q_RxLevMin; + /* q-QualMin: Q-QualMin (-43..-12) */ + if (cfg_intra->q_QualMin != -1) + asn1cCallocOne(intra->q_QualMin, cfg_intra->q_QualMin); + /* s-IntraSearchP: ReselectionThreshold (0..31) */ + intra->s_IntraSearchP = cfg_intra->s_IntraSearchP; + /* s-IntraSearchQ: ReselectionThresholdQ (0..31) */ + if (cfg_intra->s_IntraSearchQ != -1) + asn1cCallocOne(intra->s_IntraSearchQ, cfg_intra->s_IntraSearchQ); + /* t-ReselectionNR: (0..7) */ + intra->t_ReselectionNR = cfg_intra->t_ReselectionNR; + /* deriveSSB-IndexFromCell (bool) */ + intra->deriveSSB_IndexFromCell = cfg->deriveSSB_IndexFromCell; + /* smtc */ + intra->smtc = ssbmtc; + + return sib2; +} + /** @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) */ @@ -440,9 +557,17 @@ void rrc_gNB_process_f1_setup_req(f1ap_setup_req_t *req, sctp_assoc_t assoc_id) switch (sib->SIB_type) { case NR_SIB_2: { NR_SSB_MTC_t *ssbmtc = get_ssb_mtc(new->mtc); - byte_array_t ba = {0}; - ba.len = do_SIB2_NR(&ba.buf, ssbmtc); - add_si_msg(&cell, sib->SIB_type, &ba); + NR_SIB2_t *sib2 = get_sib2_from_cfg(&rrc->sib2_config, ssbmtc); + byte_array_t enc = do_SIB2_NR(sib2); + ASN_STRUCT_FREE(asn_DEF_NR_SIB2, sib2); + if (!enc.buf || enc.len == 0) { + free_byte_array(enc); + LOG_E(NR_RRC, "SIB2 encoding failed\n"); + break; + } + add_si_msg(&cell, sib->SIB_type, &enc); + 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; default: AssertFatal(false, "SIB%d not handled yet\n", sib->SIB_type);