diff --git a/CMakeLists.txt b/CMakeLists.txt index bae1c590bc..9883366ba6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1201,6 +1201,7 @@ set(NR_L2_SRC_UE ${NR_UE_RRC_DIR}/L2_interface_ue.c ${NR_UE_RRC_DIR}/main_ue.c ${NR_UE_RRC_DIR}/rrc_UE.c + ${NR_UE_RRC_DIR}/verify_RRC.c ${NR_UE_RRC_DIR}/rrc_timers_and_constants.c ${NR_UE_RRC_DIR}/rrc_sl_preconfig.c ) diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c index 672bc3c3a2..6bd3ad9fbe 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c @@ -843,10 +843,23 @@ static int nr_ue_process_dci_dl_10(NR_UE_MAC_INST_t *mac, dlsch_pdu->number_symbols = tda_info.nrOfSymbols; dlsch_pdu->start_symbol = tda_info.startSymbolIndex; - struct NR_DMRS_DownlinkConfig *dl_dmrs_config = NULL; - if (pdsch_config) - dl_dmrs_config = (tda_info.mapping_type == typeA) ? pdsch_config->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup - : pdsch_config->dmrs_DownlinkForPDSCH_MappingTypeB->choice.setup; + mappingType_t type = tda_info.mapping_type; + NR_DMRS_DownlinkConfig_t *dl_dmrs_config = NULL; + if (pdsch_config) { + if (type == typeA) { + if (!pdsch_config->dmrs_DownlinkForPDSCH_MappingTypeA) { + LOG_E(MAC, "Invalid PDSCH DMRS configuration, expected typeA but not configured\n"); + return -1; + } else + dl_dmrs_config = pdsch_config->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup; + } else { // typeB + if (!pdsch_config->dmrs_DownlinkForPDSCH_MappingTypeB) { + LOG_E(MAC, "Invalid PDSCH DMRS configuration, expected typeB but not configured\n"); + return -1; + } else + dl_dmrs_config = pdsch_config->dmrs_DownlinkForPDSCH_MappingTypeB->choice.setup; + } + } dlsch_pdu->nscid = 0; if (dl_dmrs_config && dl_dmrs_config->scramblingID0) @@ -1184,9 +1197,23 @@ static int nr_ue_process_dci_dl_11(NR_UE_MAC_INST_t *mac, dlsch_pdu->number_symbols = tda_info.nrOfSymbols; dlsch_pdu->start_symbol = tda_info.startSymbolIndex; - struct NR_DMRS_DownlinkConfig *dl_dmrs_config = (tda_info.mapping_type == typeA) - ? pdsch_Config->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup - : pdsch_Config->dmrs_DownlinkForPDSCH_MappingTypeB->choice.setup; + mappingType_t type = tda_info.mapping_type; + NR_DMRS_DownlinkConfig_t *dl_dmrs_config = NULL; + if (pdsch_Config) { + if (type == typeA) { + if (!pdsch_Config->dmrs_DownlinkForPDSCH_MappingTypeA) { + LOG_E(MAC, "Invalid PDSCH DMRS configuration, expected typeA but not configured\n"); + return -1; + } else + dl_dmrs_config = pdsch_Config->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup; + } else { // typeB + if (!pdsch_Config->dmrs_DownlinkForPDSCH_MappingTypeB) { + LOG_E(MAC, "Invalid PDSCH DMRS configuration, expected typeB but not configured\n"); + return -1; + } else + dl_dmrs_config = pdsch_Config->dmrs_DownlinkForPDSCH_MappingTypeB->choice.setup; + } + } switch (dci->dmrs_sequence_initialization.val) { case 0: @@ -1417,9 +1444,9 @@ static int nr_ue_process_dci_dl_11(NR_UE_MAC_INST_t *mac, dlsch_pdu->tbslbrm = nr_compute_tbslbrm(dlsch_pdu->mcs_table, sc_info->dl_bw_tbslbrm, nl_tbslbrm); /*PTRS configuration */ dlsch_pdu->pduBitmap = 0; - if (pdsch_Config->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS != NULL) { + if (dl_dmrs_config->phaseTrackingRS != NULL) { bool valid_ptrs_setup = - set_dl_ptrs_values(pdsch_Config->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup, + set_dl_ptrs_values(dl_dmrs_config->phaseTrackingRS->choice.setup, dlsch_pdu->number_rbs, dlsch_pdu->mcs, dlsch_pdu->mcs_table, diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c index fcc977d9e5..d06c8302a6 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c @@ -598,12 +598,21 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, } int mappingtype = tda_info->mapping_type; - NR_DMRS_UplinkConfig_t *NR_DMRS_ulconfig = NULL; - if(pusch_Config) { - NR_DMRS_ulconfig = (mappingtype == NR_PUSCH_TimeDomainResourceAllocation__mappingType_typeA) - ? pusch_Config->dmrs_UplinkForPUSCH_MappingTypeA->choice.setup - : pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup; + if (pusch_Config) { + if (mappingtype == NR_PUSCH_TimeDomainResourceAllocation__mappingType_typeA) { + if (!pusch_Config->dmrs_UplinkForPUSCH_MappingTypeA) { + LOG_E(MAC, "Invalid PUSCH DMRS configuration, expected typeA but not configured\n"); + return -1; + } else + NR_DMRS_ulconfig = pusch_Config->dmrs_UplinkForPUSCH_MappingTypeA->choice.setup; + } else { // typeB + if (!pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB) { + LOG_E(MAC, "Invalid PDSCH DMRS configuration, expected typeB but not configured\n"); + return -1; + } else + NR_DMRS_ulconfig = pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup; + } } pusch_config_pdu->scid = 0; diff --git a/openair2/RRC/NR_UE/rrc_UE.c b/openair2/RRC/NR_UE/rrc_UE.c index 4860e0b63e..2c0c4a25ac 100644 --- a/openair2/RRC/NR_UE/rrc_UE.c +++ b/openair2/RRC/NR_UE/rrc_UE.c @@ -1006,8 +1006,14 @@ static void nr_rrc_process_reconfigurationWithSync(NR_UE_RRC_INST_t *rrc, } } -static void nr_rrc_cellgroup_configuration(NR_UE_RRC_INST_t *rrc, NR_CellGroupConfig_t *cgConfig, int gNB_index, bool dedicatedsib1) +static bool nr_rrc_cellgroup_configuration(NR_UE_RRC_INST_t *rrc, NR_CellGroupConfig_t *cgConfig, int gNB_index, bool dedicatedsib1) { + if (!check_cellgroup_config(cgConfig)) { + // if we received a configuration not supported or with some wrong combination + // we call the function for RLF (re-establishment if security is activated, going to IDLE otherwise) + handle_rlf_detection(rrc); + return false; + } NR_SpCellConfig_t *spCellConfig = cgConfig->spCellConfig; if(spCellConfig) { NR_ServingCellConfig_t *spCellConfigDedicated = spCellConfig->spCellConfigDedicated; @@ -1059,14 +1065,10 @@ static void nr_rrc_cellgroup_configuration(NR_UE_RRC_INST_t *rrc, NR_CellGroupCo } nr_rrc_manage_rlc_bearers(rrc, cgConfig); - - if (cgConfig->ext1) - AssertFatal(cgConfig->ext1->reportUplinkTxDirectCurrent == NULL, "Reporting of UplinkTxDirectCurrent not implemented\n"); - AssertFatal(cgConfig->sCellToReleaseList == NULL, "Secondary serving cell release not implemented\n"); - AssertFatal(cgConfig->sCellToAddModList == NULL, "Secondary serving cell addition not implemented\n"); + return true; } -static void nr_rrc_ue_process_masterCellGroup(NR_UE_RRC_INST_t *rrc, +static bool nr_rrc_ue_process_masterCellGroup(NR_UE_RRC_INST_t *rrc, OCTET_STRING_t *masterCellGroup, long *fullConfig, int gNB_index) @@ -1080,13 +1082,18 @@ static void nr_rrc_ue_process_masterCellGroup(NR_UE_RRC_INST_t *rrc, masterCellGroup->size, 0, 0); if ((dec_rval.code != RC_OK) && (dec_rval.consumed == 0)) { LOG_E(NR_RRC, "CellGroupConfig decode error\n"); - return; + // if the ASN1 decoding fails for the received CellGroup configuration + // we call the function for RLF (re-establishment if security is activated, going to IDLE otherwise) + handle_rlf_detection(rrc); + return false; } if (LOG_DEBUGFLAG(DEBUG_ASN1)) { xer_fprint(stdout, &asn_DEF_NR_CellGroupConfig, (const void *) cellGroupConfig); } - nr_rrc_cellgroup_configuration(rrc, cellGroupConfig, gNB_index, false); + bool ret = nr_rrc_cellgroup_configuration(rrc, cellGroupConfig, gNB_index, false); + if (!ret) + return false; LOG_D(RRC, "Sending CellGroupConfig to MAC the pointer will be managed by mac\n"); nr_mac_rrc_message_t rrc_msg = {0}; @@ -1097,6 +1104,7 @@ static void nr_rrc_ue_process_masterCellGroup(NR_UE_RRC_INST_t *rrc, mac_msg->hfn = rrc->current_hfn; mac_msg->frame = rrc->current_frame; nr_rrc_send_msg_to_mac(rrc, &rrc_msg); + return true; } static bool nr_rrc_process_reconfiguration_v1530(NR_UE_RRC_INST_t *rrc, NR_RRCReconfiguration_v1530_IEs_t *rec_1530, int gNB_index) @@ -1105,8 +1113,11 @@ static bool nr_rrc_process_reconfiguration_v1530(NR_UE_RRC_INST_t *rrc, NR_RRCRe // TODO perform the full configuration procedure as specified in 5.3.5.11 of 331 LOG_E(NR_RRC, "RRCReconfiguration includes fullConfig but this is not implemented yet\n"); } - if (rec_1530->masterCellGroup) - nr_rrc_ue_process_masterCellGroup(rrc, rec_1530->masterCellGroup, rec_1530->fullConfig, gNB_index); + if (rec_1530->masterCellGroup) { + bool ret = nr_rrc_ue_process_masterCellGroup(rrc, rec_1530->masterCellGroup, rec_1530->fullConfig, gNB_index); + if (!ret) + return false; + } if (rec_1530->masterKeyUpdate) { as_security_key_update(rrc, rec_1530->masterKeyUpdate); nr_pdcp_entity_security_keys_and_algos_t sp = get_security_rrc_parameters(rrc, true); @@ -1544,11 +1555,15 @@ static void nr_rrc_ue_process_rrcReconfiguration(NR_UE_RRC_INST_t *rrc, int gNB_ LOG_E(NR_RRC, "\n"); // free the memory SEQUENCE_free(&asn_DEF_NR_CellGroupConfig, (void *)cellGroupConfig, 1); + // if the ASN1 decoding fails for the received CellGroup configuration + // we call the function for RLF (re-establishment if security is activated, going to IDLE otherwise) + handle_rlf_detection(rrc); } else { if (LOG_DEBUGFLAG(DEBUG_ASN1)) xer_fprint(stdout, &asn_DEF_NR_CellGroupConfig, (const void *) cellGroupConfig); - nr_rrc_cellgroup_configuration(rrc, cellGroupConfig, gNB_index, dedicatedsib1); + bool ret = nr_rrc_cellgroup_configuration(rrc, cellGroupConfig, gNB_index, dedicatedsib1); + AssertFatal(ret, "CellGroup has wrong configuration for the UE. Unexpected\n"); AssertFatal(!IS_SA_MODE(get_softmodem_params()), "secondaryCellGroup only used in NSA for now\n"); nr_mac_rrc_message_t rrc_msg = {0}; rrc_msg.payload_type = NR_MAC_RRC_CONFIG_CG; @@ -2152,7 +2167,9 @@ static void nr_rrc_process_rrcsetup(NR_UE_RRC_INST_t *rrc, const NR_RRCSetup_t * nr_rrc_rrcsetup_fallback(rrc); // perform the cell group configuration procedure in accordance with the received masterCellGroup - nr_rrc_ue_process_masterCellGroup(rrc, &rrcSetup->criticalExtensions.choice.rrcSetup->masterCellGroup, NULL, 0); + bool ret = nr_rrc_ue_process_masterCellGroup(rrc, &rrcSetup->criticalExtensions.choice.rrcSetup->masterCellGroup, NULL, 0); + if (!ret) + return; // perform the radio bearer configuration procedure in accordance with the received radioBearerConfig nr_rrc_ue_process_RadioBearerConfig(rrc, &rrcSetup->criticalExtensions.choice.rrcSetup->radioBearerConfig); diff --git a/openair2/RRC/NR_UE/rrc_proto.h b/openair2/RRC/NR_UE/rrc_proto.h index ab14f704b9..34b3d7e2c7 100644 --- a/openair2/RRC/NR_UE/rrc_proto.h +++ b/openair2/RRC/NR_UE/rrc_proto.h @@ -99,7 +99,7 @@ void nr_rrc_ue_decode_NR_SBCCH_SL_BCH_Message(NR_UE_RRC_INST_t *rrc, const uint16_t rx_slss_id); void nr_rrc_set_mac_queue(instance_t instance, notifiedFIFO_t *mac_input_nf); - +bool check_cellgroup_config(const NR_CellGroupConfig_t *cgConfig); /** @}*/ #endif diff --git a/openair2/RRC/NR_UE/verify_RRC.c b/openair2/RRC/NR_UE/verify_RRC.c new file mode 100644 index 0000000000..b33da53dae --- /dev/null +++ b/openair2/RRC/NR_UE/verify_RRC.c @@ -0,0 +1,314 @@ +/* + * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The OpenAirInterface Software Alliance licenses this file to You under + * the OAI Public License, Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.openairinterface.org/?page_id=698 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------------------- + * For more information about the OpenAirInterface (OAI) Software Alliance: + * contact@openairinterface.org + */ + + #include "rrc_proto.h" + +static bool check_resourcesForInterference(const NR_CSI_MeasConfig_t *meas, NR_CSI_ResourceConfigId_t res_id, bool is_CSIIM) +{ + NR_CSI_ResourceConfig_t *res = NULL; + if (meas->csi_ResourceConfigToAddModList) { + for (int i = 0; i < meas->csi_ResourceConfigToAddModList->list.count; i++) { + if (meas->csi_ResourceConfigToAddModList->list.array[i]->csi_ResourceConfigId == res_id) + res = meas->csi_ResourceConfigToAddModList->list.array[i]; + } + } + if (!res) { + LOG_E(NR_RRC, "No CSI-Resource matching with resourcesForInterference set in report config\n"); + return false; + } else { + if (is_CSIIM) { + if (res->csi_RS_ResourceSetList.present != NR_CSI_ResourceConfig__csi_RS_ResourceSetList_PR_csi_IM_ResourceSetList) { + LOG_E(NR_RRC, "resourcesForInterference doesn't point to CSI-IM resource\n"); + return false; + } + } else { + if (res->csi_RS_ResourceSetList.present != NR_CSI_ResourceConfig__csi_RS_ResourceSetList_PR_nzp_CSI_RS_SSB + || !res->csi_RS_ResourceSetList.choice.nzp_CSI_RS_SSB->nzp_CSI_RS_ResourceSetList) { + LOG_E(NR_RRC, "NZP CSI-RS resourcesForInterference doesn't point to NZP CSI-RS resource\n"); + return false; + } + } + } + return true; +} + +static bool check_resourcesForChannelMeasurement(const NR_CSI_MeasConfig_t *meas, + NR_CSI_ResourceConfigId_t res_id, + struct NR_CSI_ReportConfig__reportQuantity quantity) +{ + NR_CSI_ResourceConfig_t *res = NULL; + if (meas->csi_ResourceConfigToAddModList) { + for (int i = 0; i < meas->csi_ResourceConfigToAddModList->list.count; i++) { + if (meas->csi_ResourceConfigToAddModList->list.array[i]->csi_ResourceConfigId == res_id) + res = meas->csi_ResourceConfigToAddModList->list.array[i]; + } + } + if (!res) { + LOG_E(NR_RRC, "No CSI-Resource matching with resourcesForChannelMeasurement set in report config\n"); + return false; + } else { + if (res->csi_RS_ResourceSetList.present != NR_CSI_ResourceConfig__csi_RS_ResourceSetList_PR_nzp_CSI_RS_SSB) { + LOG_E(NR_RRC, "resourcesForChannelMeasurement doesn't point to CSI-RS-SSB resource\n"); + return false; + } else { + if (!res->csi_RS_ResourceSetList.choice.nzp_CSI_RS_SSB->nzp_CSI_RS_ResourceSetList + && (quantity.present != NR_CSI_ReportConfig__reportQuantity_PR_ssb_Index_RSRP + && quantity.present != NR_CSI_ReportConfig__reportQuantity_PR_NOTHING + && quantity.present != NR_CSI_ReportConfig__reportQuantity_PR_none)) { + LOG_E(NR_RRC, "resourcesForChannelMeasurement is for CRI measurement but doen't point to an CRI-RS resource\n"); + return false; + } + } + } + return true; +} + +static bool check_csi_resourceMapping_consistency(NR_CSI_RS_ResourceMapping_t resourceMapping) +{ + bool valid = true; + struct NR_CSI_RS_ResourceMapping__density *density = &resourceMapping.density; + switch(resourceMapping.frequencyDomainAllocation.present) { + case NR_CSI_RS_ResourceMapping__frequencyDomainAllocation_PR_row1: + if (resourceMapping.nrofPorts != NR_CSI_RS_ResourceMapping__nrofPorts_p1) + valid = false; + else if (resourceMapping.cdm_Type != NR_CSI_RS_ResourceMapping__cdm_Type_noCDM) + valid = false; + else if (density->present != NR_CSI_RS_ResourceMapping__density_PR_three + && density->present != NR_CSI_RS_ResourceMapping__density_PR_NOTHING) + valid = false; + break; + case NR_CSI_RS_ResourceMapping__frequencyDomainAllocation_PR_row2: + if (resourceMapping.nrofPorts != NR_CSI_RS_ResourceMapping__nrofPorts_p1) + valid = false; + else if (resourceMapping.cdm_Type != NR_CSI_RS_ResourceMapping__cdm_Type_noCDM) + valid = false; + else if (density->present != NR_CSI_RS_ResourceMapping__density_PR_dot5 + && density->present != NR_CSI_RS_ResourceMapping__density_PR_one) + valid = false; + break; + case NR_CSI_RS_ResourceMapping__frequencyDomainAllocation_PR_row4: + if (resourceMapping.nrofPorts != NR_CSI_RS_ResourceMapping__nrofPorts_p4) + valid = false; + else if (resourceMapping.cdm_Type != NR_CSI_RS_ResourceMapping__cdm_Type_fd_CDM2) + valid = false; + else if (density->present != NR_CSI_RS_ResourceMapping__density_PR_one + && density->present != NR_CSI_RS_ResourceMapping__density_PR_NOTHING) + valid = false; + break; + case NR_CSI_RS_ResourceMapping__frequencyDomainAllocation_PR_other: + switch(resourceMapping.nrofPorts) { + case NR_CSI_RS_ResourceMapping__nrofPorts_p1: + valid = false; // 1 port is row 1 or 2 not other + break; + case NR_CSI_RS_ResourceMapping__nrofPorts_p2: + if (resourceMapping.cdm_Type != NR_CSI_RS_ResourceMapping__cdm_Type_fd_CDM2) + valid = false; + else if (density->present != NR_CSI_RS_ResourceMapping__density_PR_dot5 + && density->present != NR_CSI_RS_ResourceMapping__density_PR_one) + valid = false; + break; + case NR_CSI_RS_ResourceMapping__nrofPorts_p4: + // row 5 -> l0+1 + if (resourceMapping.firstOFDMSymbolInTimeDomain == 13) + valid = false; + else if (resourceMapping.cdm_Type != NR_CSI_RS_ResourceMapping__cdm_Type_fd_CDM2) + valid = false; + else if (density->present != NR_CSI_RS_ResourceMapping__density_PR_one + && density->present != NR_CSI_RS_ResourceMapping__density_PR_NOTHING) + valid = false; + break; + case NR_CSI_RS_ResourceMapping__nrofPorts_p8: { + uint8_t freq = BIT_STRING_to_uint8(&resourceMapping.frequencyDomainAllocation.choice.other); + if (resourceMapping.cdm_Type == NR_CSI_RS_ResourceMapping__cdm_Type_cdm4_FD2_TD2 || count_bits(&freq, 1) != 4) { + // row 7 and 8 -> l0+1 + if (resourceMapping.firstOFDMSymbolInTimeDomain == 13) + valid = false; + } + if (resourceMapping.cdm_Type != NR_CSI_RS_ResourceMapping__cdm_Type_fd_CDM2 + && resourceMapping.cdm_Type != NR_CSI_RS_ResourceMapping__cdm_Type_cdm4_FD2_TD2) + valid = false; + else if (density->present != NR_CSI_RS_ResourceMapping__density_PR_one + && density->present != NR_CSI_RS_ResourceMapping__density_PR_NOTHING) + valid = false; + break; + } + case NR_CSI_RS_ResourceMapping__nrofPorts_p12: + if (resourceMapping.cdm_Type == NR_CSI_RS_ResourceMapping__cdm_Type_cdm4_FD2_TD2) { + // row 10 -> l0+1 + if (resourceMapping.firstOFDMSymbolInTimeDomain == 13) + valid = false; + } else if (resourceMapping.cdm_Type != NR_CSI_RS_ResourceMapping__cdm_Type_fd_CDM2) + valid = false; + else if (density->present != NR_CSI_RS_ResourceMapping__density_PR_one + && density->present != NR_CSI_RS_ResourceMapping__density_PR_NOTHING) + valid = false; + break; + case NR_CSI_RS_ResourceMapping__nrofPorts_p16: + // l0 + 1 in all cases + if (resourceMapping.firstOFDMSymbolInTimeDomain == 13) + valid = false; + else if (resourceMapping.cdm_Type != NR_CSI_RS_ResourceMapping__cdm_Type_fd_CDM2 + && resourceMapping.cdm_Type != NR_CSI_RS_ResourceMapping__cdm_Type_cdm4_FD2_TD2) + valid = false; + else if (density->present != NR_CSI_RS_ResourceMapping__density_PR_dot5 + && density->present != NR_CSI_RS_ResourceMapping__density_PR_one) + valid = false; + case NR_CSI_RS_ResourceMapping__nrofPorts_p24: + case NR_CSI_RS_ResourceMapping__nrofPorts_p32: + if (resourceMapping.cdm_Type == NR_CSI_RS_ResourceMapping__cdm_Type_noCDM) + valid = false; + else if (resourceMapping.cdm_Type == NR_CSI_RS_ResourceMapping__cdm_Type_cdm4_FD2_TD2 + || resourceMapping.cdm_Type == NR_CSI_RS_ResourceMapping__cdm_Type_fd_CDM2) { + // row 13, 14 and 16, 17 -> l0+1 and l1 (which should be larger than l0+1 + if (resourceMapping.firstOFDMSymbolInTimeDomain == 13) + valid = false; + if (!resourceMapping.firstOFDMSymbolInTimeDomain2 + || *resourceMapping.firstOFDMSymbolInTimeDomain2 <= resourceMapping.firstOFDMSymbolInTimeDomain + 1) + valid = false; + } else { + // row 15 and 18 -> l0+3 + if (resourceMapping.firstOFDMSymbolInTimeDomain > 10) + valid = false; + } + if (valid + && (density->present != NR_CSI_RS_ResourceMapping__density_PR_dot5 + && density->present != NR_CSI_RS_ResourceMapping__density_PR_one)) + valid = false; + break; + default: + valid = false; + } + break; + default: + valid = false; + } + if (!valid) + return false; + return true; +} + +static bool check_csi_report_consistency(const NR_CSI_MeasConfig_t *meas) +{ + if (!meas || !meas->csi_ReportConfigToAddModList) + return true; + for (int i = 0; i < meas->csi_ReportConfigToAddModList->list.count; i++) { + NR_CSI_ReportConfig_t *csirep = meas->csi_ReportConfigToAddModList->list.array[i]; + if (!check_resourcesForChannelMeasurement(meas, csirep->resourcesForChannelMeasurement, csirep->reportQuantity)) + return false; + if (csirep->csi_IM_ResourcesForInterference) { + if (!check_resourcesForInterference(meas, *csirep->csi_IM_ResourcesForInterference, true)) + return false; + } + if (csirep->nzp_CSI_RS_ResourcesForInterference) { + if (!check_resourcesForInterference(meas, *csirep->nzp_CSI_RS_ResourcesForInterference, false)) + return false; + } + } + return true; +} + +static bool check_csi_resource_consistency(const NR_CSI_MeasConfig_t *meas) +{ + if (!meas || !meas->nzp_CSI_RS_ResourceToAddModList) + return true; + for (int i = 0; i < meas->nzp_CSI_RS_ResourceToAddModList->list.count; i++) { + NR_NZP_CSI_RS_Resource_t *res = meas->nzp_CSI_RS_ResourceToAddModList->list.array[i]; + if (!check_csi_resourceMapping_consistency(res->resourceMapping)) + return false; + } + return true; +} + +static bool check_csi_MeasConfig(struct NR_SetupRelease_CSI_MeasConfig *csi_MeasConfig) +{ + const NR_CSI_MeasConfig_t *meas = csi_MeasConfig->choice.setup; + if (!check_csi_report_consistency(meas)) + return false; + if (!check_csi_resource_consistency(meas)) + return false; + return true; +} + +static bool check_srs_config(NR_SRS_Config_t *srs_Config) +{ + if (srs_Config->srs_ResourceToAddModList) { + for (int i = 0; i < srs_Config->srs_ResourceToAddModList->list.count; i++) { + NR_SRS_Resource_t *res = srs_Config->srs_ResourceToAddModList->list.array[i]; + int start = NR_NUMBER_OF_SYMBOLS_PER_SLOT - res->resourceMapping.startPosition - 1; + int num = 1 << res->resourceMapping.nrofSymbols; + if (start + num > NR_NUMBER_OF_SYMBOLS_PER_SLOT) { + LOG_E(NR_RRC, "The configured SRS resource exceeds the slot boundary\n"); + return false; + } + } + } + return true; +} + +static bool check_ul_bwp_config(NR_BWP_UplinkDedicated_t *bwp_Config) +{ + if (bwp_Config->srs_Config && bwp_Config->srs_Config->choice.setup) { + if (!check_srs_config(bwp_Config->srs_Config->choice.setup)) + return false; + } + return true; +} + +bool check_cellgroup_config(const NR_CellGroupConfig_t *cgConfig) +{ + if (cgConfig->spCellConfig && cgConfig->spCellConfig->spCellConfigDedicated) { + NR_ServingCellConfig_t *spCellConfigDedicated = cgConfig->spCellConfig->spCellConfigDedicated; + if (spCellConfigDedicated->csi_MeasConfig) { + if (!check_csi_MeasConfig(spCellConfigDedicated->csi_MeasConfig)) + return false; + } + if (spCellConfigDedicated->uplinkConfig) { + NR_UplinkConfig_t *ul_Config = spCellConfigDedicated->uplinkConfig; + if (ul_Config->initialUplinkBWP) { + if (!check_ul_bwp_config(ul_Config->initialUplinkBWP)) + return false; + } + if (ul_Config->uplinkBWP_ToAddModList) { + for (int i = 0; i < ul_Config->uplinkBWP_ToAddModList->list.count; i++) { + NR_BWP_Uplink_t *ul_bwp = ul_Config->uplinkBWP_ToAddModList->list.array[i]; + if (ul_bwp->bwp_Dedicated) { + if (!check_ul_bwp_config(ul_bwp->bwp_Dedicated)) + return false; + } + } + } + } + } + + if (cgConfig->ext1 && cgConfig->ext1->reportUplinkTxDirectCurrent) { + LOG_E(NR_RRC, "Reporting of UplinkTxDirectCurrent not implemented\n"); + return false; + } + if (cgConfig->sCellToReleaseList) { + LOG_E(NR_RRC, "Secondary serving cell release not implemented\n"); + return false; + } + if (cgConfig->sCellToAddModList) { + LOG_E(NR_RRC, "Secondary serving cell addition not implemented\n"); + return false; + } + return true; +}